We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Results filter inconsistency, Old vs. New results tab


--- 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/views/project.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 """Akvo RSR is covered by the GNU Affero General Public License.
3 
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 
8 from django.db.models import Q
9 from django.shortcuts import get_object_or_404
10 from django.http import Http404
11 from rest_framework.authentication import SessionAuthentication
12 from rest_framework.decorators import api_view, authentication_classes, permission_classes
13 from rest_framework.permissions import IsAuthenticated
14 from rest_framework.response import Response
15 from rest_framework.status import HTTP_201_CREATED
16 from geojson import Feature, Point, FeatureCollection
17 
18 from akvo.codelists.store.default_codelists import SECTOR_CATEGORY
19 from akvo.rest.cache import serialized_project
20 from akvo.rest.serializers import (ProjectSerializer, ProjectExtraSerializer,
21                                    ProjectExtraDeepSerializer,
22                                    ProjectIatiExportSerializer,
23                                    ProjectUpSerializer,
24                                    TypeaheadOrganisationSerializer,
25                                    ProjectMetadataSerializer,
26                                    OrganisationCustomFieldSerializer,
27                                    ProjectHierarchyRootSerializer,
28                                    ProjectHierarchyTreeSerializer,)
29 from akvo.rest.models import TastyTokenAuthentication
30 from akvo.rsr.models import Project, OrganisationCustomField, IndicatorPeriodData
31 from akvo.rsr.views.my_rsr import user_viewable_projects
32 from akvo.utils import codelist_choices
33 from ..viewsets import PublicProjectViewSet, ReadOnlyPublicProjectViewSet
34 
35 
36 class ProjectViewSet(PublicProjectViewSet):
37 
38     """
39     Viewset providing Project data.
40     """
41     queryset = Project.objects.prefetch_related(
42         'publishingstatus',
43         'categories',
44         'keywords',
45         'partners',
46     )
47     serializer_class = ProjectSerializer
48     project_relation = ''
49 
50     def get_queryset(self):
51         """
52         Allow custom filter for sync_owner, since this field has been replaced by the
53         reporting org partnership.
54         """
55 
56         sync_owner = self.request.query_params.get('sync_owner', None)
57         reporting_org = self.request.query_params.get('reporting_org', None)
58 
59         reporting_org = reporting_org or sync_owner
60         if reporting_org:
61             self.queryset = self.queryset.filter(
62                 partnerships__iati_organisation_role=101,
63                 partnerships__organisation__pk=reporting_org
64             ).distinct()
65         return super(ProjectViewSet, self).get_queryset()
66 
67 
68 class MyProjectsViewSet(PublicProjectViewSet):
69     """Viewset providing listing of projects a user can edit."""
70     queryset = Project.objects.all().select_related('publishingstatus')\
71         .prefetch_related('locations', 'categories', 'related_projects', 'recipient_countries')
72     serializer_class = ProjectMetadataSerializer
73     project_relation = ''
74 
75     def get_queryset(self):
76         if self.request.user.is_anonymous:
77             return Project.objects.none()
78         show_restricted = bool(self.request.query_params.get('show_restricted'))
79 
80         filter_program = self.request.query_params.get('filter_program')
81         if filter_program:
82             try:
83                 filter_program = int(filter_program)
84             except ValueError:
85                 filter_program = None
86 
87         queryset = user_viewable_projects(self.request.user, show_restricted, filter_program)
88 
89         sector = self.request.query_params.get('sector', None)
90         if sector:
91             queryset = queryset.filter(sectors__sector_code=sector)
92         country = self.request.query_params.get('country', None)
93         if country:
94             queryset = queryset.filter(locations__country__iso_code=country)\
95                 .union(queryset.filter(recipient_countries__country__iexact=country))
96         return queryset
97 
98 
99 class ProjectHierarchyViewSet(ReadOnlyPublicProjectViewSet):
100     queryset = Project.objects.none()
101     serializer_class = ProjectHierarchyRootSerializer
102     project_relation = ''
103 
104     def get_queryset(self):
105         if self.request.user.is_anonymous:
106             return Project.objects.none()
107         queryset = self.request.user.my_projects()\
108                                     .published()\
109                                     .filter(projecthierarchy__isnull=False)
110         return queryset
111 
112     def retrieve(self, request, *args, **kwargs):
113         project = get_object_or_404(Project, pk=self.kwargs['pk'])
114         root = project.ancestor()
115         if not self.request.user.has_perm('rsr.view_project', root):
116             raise Http404
117 
118         serializer = ProjectHierarchyTreeSerializer(root, context=self.get_serializer_context())
119 
120         return Response(serializer.data)
121 
122 
123 class ProjectIatiExportViewSet(PublicProjectViewSet):
124     """Lean viewset for project data, as used in the My IATI section of RSR."""
125     queryset = Project.objects.only(
126         'id',
127         'title',
128         'is_public',
129         'status',
130         'run_iati_checks',
131     ).prefetch_related(
132         'partners',
133         'iati_checks',
134         'publishingstatus',
135         'partnerships',
136     )
137     serializer_class = ProjectIatiExportSerializer
138     project_relation = ''
139     paginate_by_param = 'limit'
140     max_paginate_by = 50
141 
142     def get_queryset(self):
143         """
144         Allow custom filter for sync_owner, since this field has been replaced by the
145         reporting org partnership.
146         """
147         reporting_org = self.request.query_params.get('reporting_org', None)
148         if reporting_org:
149             self.queryset = self.queryset.filter(
150                 partnerships__iati_organisation_role=101,
151                 partnerships__organisation__pk=reporting_org
152             ).distinct()
153         return super(ProjectIatiExportViewSet, self).get_queryset()
154 
155     def list(self, request, *args, **kwargs):
156         projects = self.queryset.filter(run_iati_checks=True)
157         for project in projects:
158             project.update_iati_checks()
159         return super(ProjectIatiExportViewSet, self).list(request, *args, **kwargs)
160 
161 
162 class ProjectExtraViewSet(ProjectViewSet):
163     r"""
164     Viewset providing extra Project data.
165 
166     Allowed parameters are:
167     __limit__ (default 10, max 30),
168     __partnerships\__organisation__ (filter on organisation ID), and
169     __publishingstatus\__status__ (filter on publishing status)
170     """
171 
172     queryset = Project.objects.prefetch_related(
173         'publishingstatus',
174         'sectors',
175         'partnerships',
176         'budget_items',
177         'legacy_data',
178         'links',
179         'locations',
180         'locations__country',
181         'planned_disbursements',
182         'policy_markers',
183         'documents',
184         'conditions',
185         'contacts',
186         'project_updates',
187         'recipient_countries',
188         'recipient_regions',
189         'related_projects',
190         'results',
191         'sectors',
192         'transactions',
193     )
194     serializer_class = ProjectExtraSerializer
195     paginate_by_param = 'limit'
196     paginate_by = 10
197     max_paginate_by = 30
198 
199 
200 class ProjectExtraDeepViewSet(ProjectViewSet):
201     r"""
202     Viewset providing extra deep (depth=2 or bigger) Project data.
203 
204     Allowed parameters are:
205     __limit__ (default 5, max 10),
206     __partnerships\__organisation__ (filter on organisation ID), and
207     __publishingstatus\__status__ (filter on publishing status)
208     """
209 
210     queryset = Project.objects.prefetch_related(
211         'publishingstatus',
212         'sectors',
213         'partnerships',
214         'budget_items',
215         'legacy_data',
216         'links',
217         'locations',
218         'locations__country',
219         'planned_disbursements',
220         'policy_markers',
221         'documents',
222         'conditions',
223         'contacts',
224         'project_updates',
225         'recipient_countries',
226         'recipient_regions',
227         'related_projects',
228         'results',
229         'sectors',
230         'transactions',
231     )
232     serializer_class = ProjectExtraDeepSerializer
233     paginate_by_param = 'limit'
234     paginate_by = 5
235     max_paginate_by = 10
236 
237 
238 class ProjectUpViewSet(ProjectViewSet):
239     r"""
240     Viewset providing extra data and limited filtering for Up in one go.
241 
242     Allowed parameters are:
243     __limit__ (default 30, max 100),
244     __partnerships\__organisation__ (filter on organisation ID), and
245     __publishingstatus\__status__ (filter on publishing status)
246     """
247 
248     queryset = Project.objects.select_related(
249         'primary_location',
250     ).prefetch_related(
251         'publishingstatus',
252         'project_updates',
253         'categories',
254         'keywords',
255         'partners',
256     )
257     serializer_class = ProjectUpSerializer
258     paginate_by_param = 'limit'
259     max_paginate_by = 100
260 
261 
262 ###############################################################################
263 # Project directory
264 ###############################################################################
265 
266 @api_view(['GET'])
267 def project_directory(request):
268     """Return the values for various project filters.
269 
270     Based on the current filters, it returns new options for all the (other)
271     filters. This is used to generate dynamic filters.
272 
273     """
274 
275     page = request.rsr_page
276     projects = _project_list(request)
277     projects_data = [
278         serialized_project(project_id) for project_id in projects.values_list('pk', flat=True)
279     ]
280     organisations = list(projects.all_partners().values('id', 'name', 'long_name'))
281     organisations = TypeaheadOrganisationSerializer(organisations, many=True).data
282 
283     custom_fields = (
284         OrganisationCustomField.objects.filter(type='dropdown',
285                                                organisation=page.organisation,
286                                                show_in_searchbar=True).order_by('order', 'id')
287         if page else []
288     )
289     sectors = [{'id': id_, 'name': name} for id_, name in codelist_choices(SECTOR_CATEGORY)]
290     response = {
291         'projects': projects_data,
292         'organisation': organisations,
293         'sector': sectors,
294         'custom_fields': OrganisationCustomFieldSerializer(custom_fields, many=True).data,
295     }
296 
297     return Response(response)
298 
299 
300 def _project_list(request):
301     """Return a project queryset based on the request"""
302     # Fetch projects based on whether we are an Akvo site or RSR main site
303     page = request.rsr_page
304     projects = page.projects() if page else Project.objects.all().public().published()
305 
306     if not page:
307         # Exclude projects which don't have an image or a title for RSR site
308         projects = projects.exclude(Q(title='') | Q(current_image=''))
309     else:
310         # On partner sites, all projects show up. Partners are expected to fix
311         # their data to fix their pages!
312         pass
313     return projects
314 
315 
316 @api_view(['GET'])
317 def project_location_geojson(request):
318     """Return a GeoJSON with all the project locations."""
319     projects = _project_list(request).prefetch_related('locations')
320     features = [
321         Feature(geometry=Point((location.longitude, location.latitude)),
322                 properties=dict(
323                     project_title=project.title,
324                     project_subtitle=project.subtitle,
325                     project_url=request.build_absolute_uri(project.get_absolute_url()),
326                     project_id=project.pk,
327                     name=location.name,
328                     description=location.description))
329         for project in projects
330         for location in project.locations.all()
331         if location.is_valid()
332     ]
333     response = FeatureCollection(features)
334     return Response(response)
335 
336 
337 @api_view(['POST'])
338 @permission_classes((IsAuthenticated, ))
339 @authentication_classes([SessionAuthentication, TastyTokenAuthentication])
340 def add_project_to_program(request, program_pk):
341     program = get_object_or_404(Project, pk=program_pk)
342     parent_pk = request.data.get('parent')
343     if parent_pk is not None:
344         program = get_object_or_404(Project, pk=parent_pk)
345 
346     project = Project.objects.create()
347     Project.new_project_created(project.id, request.user)   # Log creation
348     project.add_to_program(program)
349     # Set user's primary org as accountable partner
350     org = request.user.first_organisation()
351     if org is not None and org != program.reporting_org:
352         project.set_accountable_partner(org)
353     response = ProjectSerializer(project, context=dict(request=request)).data
354     return Response(response, status=HTTP_201_CREATED)
355 
356 
357 @api_view(['GET'])
358 def project_title(request, project_pk):
359     project = get_object_or_404(Project, pk=project_pk)
360     data = {
361         'title': project.title,
362         'publishing_status': project.publishingstatus.status,
363         'has_hierarchy': project.parents_all().exists() or project.is_hierarchy_root(),
364         'pending_update_count': IndicatorPeriodData.objects.filter(
365             period__indicator__result__project=project,
366             status=IndicatorPeriodData.STATUS_PENDING_CODE
367         ).count()
368     }
369     return Response(data)
370
```
--- 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/views/project.py b/akvo/rest/views/project.py
--- a/akvo/rest/views/project.py
+++ b/akvo/rest/views/project.py
@@ -29,7 +29,7 @@
 from akvo.rest.models import TastyTokenAuthentication
 from akvo.rsr.models import Project, OrganisationCustomField, IndicatorPeriodData
 from akvo.rsr.views.my_rsr import user_viewable_projects
-from akvo.utils import codelist_choices
+from akvo.utils import codelist_choices, single_period_dates
 from ..viewsets import PublicProjectViewSet, ReadOnlyPublicProjectViewSet
 
 
@@ -357,6 +357,9 @@
 @api_view(['GET'])
 def project_title(request, project_pk):
     project = get_object_or_404(Project, pk=project_pk)
+    hierarchy_name = project.uses_single_indicator_period()
+    needs_reporting_timeout_days, _, _ = single_period_dates(hierarchy_name) if hierarchy_name else (None, None, None)
+
     data = {
         'title': project.title,
         'publishing_status': project.publishingstatus.status,
@@ -364,6 +367,7 @@
         'pending_update_count': IndicatorPeriodData.objects.filter(
             period__indicator__result__project=project,
             status=IndicatorPeriodData.STATUS_PENDING_CODE
-        ).count()
+        ).count(),
+        'needs_reporting_timeout_days': needs_reporting_timeout_days,
     }
     return Response(data)

{"golden_diff": "diff --git a/akvo/rest/views/project.py b/akvo/rest/views/project.py\n--- a/akvo/rest/views/project.py\n+++ b/akvo/rest/views/project.py\n@@ -29,7 +29,7 @@\n from akvo.rest.models import TastyTokenAuthentication\n from akvo.rsr.models import Project, OrganisationCustomField, IndicatorPeriodData\n from akvo.rsr.views.my_rsr import user_viewable_projects\n-from akvo.utils import codelist_choices\n+from akvo.utils import codelist_choices, single_period_dates\n from ..viewsets import PublicProjectViewSet, ReadOnlyPublicProjectViewSet\n \n \n@@ -357,6 +357,9 @@\n @api_view(['GET'])\n def project_title(request, project_pk):\n     project = get_object_or_404(Project, pk=project_pk)\n+    hierarchy_name = project.uses_single_indicator_period()\n+    needs_reporting_timeout_days, _, _ = single_period_dates(hierarchy_name) if hierarchy_name else (None, None, None)\n+\n     data = {\n         'title': project.title,\n         'publishing_status': project.publishingstatus.status,\n@@ -364,6 +367,7 @@\n         'pending_update_count': IndicatorPeriodData.objects.filter(\n             period__indicator__result__project=project,\n             status=IndicatorPeriodData.STATUS_PENDING_CODE\n-        ).count()\n+        ).count(),\n+        'needs_reporting_timeout_days': needs_reporting_timeout_days,\n     }\n     return Response(data)\n", "issue": "Results filter inconsistency, Old vs. New results tab\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\"\"\"Akvo RSR is covered by the GNU Affero General Public License.\n\nSee more details in the license.txt file located at the root folder of the Akvo RSR module.\nFor additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.\n\"\"\"\n\nfrom django.db.models import Q\nfrom django.shortcuts import get_object_or_404\nfrom django.http import Http404\nfrom rest_framework.authentication import SessionAuthentication\nfrom rest_framework.decorators import api_view, authentication_classes, permission_classes\nfrom rest_framework.permissions import IsAuthenticated\nfrom rest_framework.response import Response\nfrom rest_framework.status import HTTP_201_CREATED\nfrom geojson import Feature, Point, FeatureCollection\n\nfrom akvo.codelists.store.default_codelists import SECTOR_CATEGORY\nfrom akvo.rest.cache import serialized_project\nfrom akvo.rest.serializers import (ProjectSerializer, ProjectExtraSerializer,\n                                   ProjectExtraDeepSerializer,\n                                   ProjectIatiExportSerializer,\n                                   ProjectUpSerializer,\n                                   TypeaheadOrganisationSerializer,\n                                   ProjectMetadataSerializer,\n                                   OrganisationCustomFieldSerializer,\n                                   ProjectHierarchyRootSerializer,\n                                   ProjectHierarchyTreeSerializer,)\nfrom akvo.rest.models import TastyTokenAuthentication\nfrom akvo.rsr.models import Project, OrganisationCustomField, IndicatorPeriodData\nfrom akvo.rsr.views.my_rsr import user_viewable_projects\nfrom akvo.utils import codelist_choices\nfrom ..viewsets import PublicProjectViewSet, ReadOnlyPublicProjectViewSet\n\n\nclass ProjectViewSet(PublicProjectViewSet):\n\n    \"\"\"\n    Viewset providing Project data.\n    \"\"\"\n    queryset = Project.objects.prefetch_related(\n        'publishingstatus',\n        'categories',\n        'keywords',\n        'partners',\n    )\n    serializer_class = ProjectSerializer\n    project_relation = ''\n\n    def get_queryset(self):\n        \"\"\"\n        Allow custom filter for sync_owner, since this field has been replaced by the\n        reporting org partnership.\n        \"\"\"\n\n        sync_owner = self.request.query_params.get('sync_owner', None)\n        reporting_org = self.request.query_params.get('reporting_org', None)\n\n        reporting_org = reporting_org or sync_owner\n        if reporting_org:\n            self.queryset = self.queryset.filter(\n                partnerships__iati_organisation_role=101,\n                partnerships__organisation__pk=reporting_org\n            ).distinct()\n        return super(ProjectViewSet, self).get_queryset()\n\n\nclass MyProjectsViewSet(PublicProjectViewSet):\n    \"\"\"Viewset providing listing of projects a user can edit.\"\"\"\n    queryset = Project.objects.all().select_related('publishingstatus')\\\n        .prefetch_related('locations', 'categories', 'related_projects', 'recipient_countries')\n    serializer_class = ProjectMetadataSerializer\n    project_relation = ''\n\n    def get_queryset(self):\n        if self.request.user.is_anonymous:\n            return Project.objects.none()\n        show_restricted = bool(self.request.query_params.get('show_restricted'))\n\n        filter_program = self.request.query_params.get('filter_program')\n        if filter_program:\n            try:\n                filter_program = int(filter_program)\n            except ValueError:\n                filter_program = None\n\n        queryset = user_viewable_projects(self.request.user, show_restricted, filter_program)\n\n        sector = self.request.query_params.get('sector', None)\n        if sector:\n            queryset = queryset.filter(sectors__sector_code=sector)\n        country = self.request.query_params.get('country', None)\n        if country:\n            queryset = queryset.filter(locations__country__iso_code=country)\\\n                .union(queryset.filter(recipient_countries__country__iexact=country))\n        return queryset\n\n\nclass ProjectHierarchyViewSet(ReadOnlyPublicProjectViewSet):\n    queryset = Project.objects.none()\n    serializer_class = ProjectHierarchyRootSerializer\n    project_relation = ''\n\n    def get_queryset(self):\n        if self.request.user.is_anonymous:\n            return Project.objects.none()\n        queryset = self.request.user.my_projects()\\\n                                    .published()\\\n                                    .filter(projecthierarchy__isnull=False)\n        return queryset\n\n    def retrieve(self, request, *args, **kwargs):\n        project = get_object_or_404(Project, pk=self.kwargs['pk'])\n        root = project.ancestor()\n        if not self.request.user.has_perm('rsr.view_project', root):\n            raise Http404\n\n        serializer = ProjectHierarchyTreeSerializer(root, context=self.get_serializer_context())\n\n        return Response(serializer.data)\n\n\nclass ProjectIatiExportViewSet(PublicProjectViewSet):\n    \"\"\"Lean viewset for project data, as used in the My IATI section of RSR.\"\"\"\n    queryset = Project.objects.only(\n        'id',\n        'title',\n        'is_public',\n        'status',\n        'run_iati_checks',\n    ).prefetch_related(\n        'partners',\n        'iati_checks',\n        'publishingstatus',\n        'partnerships',\n    )\n    serializer_class = ProjectIatiExportSerializer\n    project_relation = ''\n    paginate_by_param = 'limit'\n    max_paginate_by = 50\n\n    def get_queryset(self):\n        \"\"\"\n        Allow custom filter for sync_owner, since this field has been replaced by the\n        reporting org partnership.\n        \"\"\"\n        reporting_org = self.request.query_params.get('reporting_org', None)\n        if reporting_org:\n            self.queryset = self.queryset.filter(\n                partnerships__iati_organisation_role=101,\n                partnerships__organisation__pk=reporting_org\n            ).distinct()\n        return super(ProjectIatiExportViewSet, self).get_queryset()\n\n    def list(self, request, *args, **kwargs):\n        projects = self.queryset.filter(run_iati_checks=True)\n        for project in projects:\n            project.update_iati_checks()\n        return super(ProjectIatiExportViewSet, self).list(request, *args, **kwargs)\n\n\nclass ProjectExtraViewSet(ProjectViewSet):\n    r\"\"\"\n    Viewset providing extra Project data.\n\n    Allowed parameters are:\n    __limit__ (default 10, max 30),\n    __partnerships\\__organisation__ (filter on organisation ID), and\n    __publishingstatus\\__status__ (filter on publishing status)\n    \"\"\"\n\n    queryset = Project.objects.prefetch_related(\n        'publishingstatus',\n        'sectors',\n        'partnerships',\n        'budget_items',\n        'legacy_data',\n        'links',\n        'locations',\n        'locations__country',\n        'planned_disbursements',\n        'policy_markers',\n        'documents',\n        'conditions',\n        'contacts',\n        'project_updates',\n        'recipient_countries',\n        'recipient_regions',\n        'related_projects',\n        'results',\n        'sectors',\n        'transactions',\n    )\n    serializer_class = ProjectExtraSerializer\n    paginate_by_param = 'limit'\n    paginate_by = 10\n    max_paginate_by = 30\n\n\nclass ProjectExtraDeepViewSet(ProjectViewSet):\n    r\"\"\"\n    Viewset providing extra deep (depth=2 or bigger) Project data.\n\n    Allowed parameters are:\n    __limit__ (default 5, max 10),\n    __partnerships\\__organisation__ (filter on organisation ID), and\n    __publishingstatus\\__status__ (filter on publishing status)\n    \"\"\"\n\n    queryset = Project.objects.prefetch_related(\n        'publishingstatus',\n        'sectors',\n        'partnerships',\n        'budget_items',\n        'legacy_data',\n        'links',\n        'locations',\n        'locations__country',\n        'planned_disbursements',\n        'policy_markers',\n        'documents',\n        'conditions',\n        'contacts',\n        'project_updates',\n        'recipient_countries',\n        'recipient_regions',\n        'related_projects',\n        'results',\n        'sectors',\n        'transactions',\n    )\n    serializer_class = ProjectExtraDeepSerializer\n    paginate_by_param = 'limit'\n    paginate_by = 5\n    max_paginate_by = 10\n\n\nclass ProjectUpViewSet(ProjectViewSet):\n    r\"\"\"\n    Viewset providing extra data and limited filtering for Up in one go.\n\n    Allowed parameters are:\n    __limit__ (default 30, max 100),\n    __partnerships\\__organisation__ (filter on organisation ID), and\n    __publishingstatus\\__status__ (filter on publishing status)\n    \"\"\"\n\n    queryset = Project.objects.select_related(\n        'primary_location',\n    ).prefetch_related(\n        'publishingstatus',\n        'project_updates',\n        'categories',\n        'keywords',\n        'partners',\n    )\n    serializer_class = ProjectUpSerializer\n    paginate_by_param = 'limit'\n    max_paginate_by = 100\n\n\n###############################################################################\n# Project directory\n###############################################################################\n\n@api_view(['GET'])\ndef project_directory(request):\n    \"\"\"Return the values for various project filters.\n\n    Based on the current filters, it returns new options for all the (other)\n    filters. This is used to generate dynamic filters.\n\n    \"\"\"\n\n    page = request.rsr_page\n    projects = _project_list(request)\n    projects_data = [\n        serialized_project(project_id) for project_id in projects.values_list('pk', flat=True)\n    ]\n    organisations = list(projects.all_partners().values('id', 'name', 'long_name'))\n    organisations = TypeaheadOrganisationSerializer(organisations, many=True).data\n\n    custom_fields = (\n        OrganisationCustomField.objects.filter(type='dropdown',\n                                               organisation=page.organisation,\n                                               show_in_searchbar=True).order_by('order', 'id')\n        if page else []\n    )\n    sectors = [{'id': id_, 'name': name} for id_, name in codelist_choices(SECTOR_CATEGORY)]\n    response = {\n        'projects': projects_data,\n        'organisation': organisations,\n        'sector': sectors,\n        'custom_fields': OrganisationCustomFieldSerializer(custom_fields, many=True).data,\n    }\n\n    return Response(response)\n\n\ndef _project_list(request):\n    \"\"\"Return a project queryset based on the request\"\"\"\n    # Fetch projects based on whether we are an Akvo site or RSR main site\n    page = request.rsr_page\n    projects = page.projects() if page else Project.objects.all().public().published()\n\n    if not page:\n        # Exclude projects which don't have an image or a title for RSR site\n        projects = projects.exclude(Q(title='') | Q(current_image=''))\n    else:\n        # On partner sites, all projects show up. Partners are expected to fix\n        # their data to fix their pages!\n        pass\n    return projects\n\n\n@api_view(['GET'])\ndef project_location_geojson(request):\n    \"\"\"Return a GeoJSON with all the project locations.\"\"\"\n    projects = _project_list(request).prefetch_related('locations')\n    features = [\n        Feature(geometry=Point((location.longitude, location.latitude)),\n                properties=dict(\n                    project_title=project.title,\n                    project_subtitle=project.subtitle,\n                    project_url=request.build_absolute_uri(project.get_absolute_url()),\n                    project_id=project.pk,\n                    name=location.name,\n                    description=location.description))\n        for project in projects\n        for location in project.locations.all()\n        if location.is_valid()\n    ]\n    response = FeatureCollection(features)\n    return Response(response)\n\n\n@api_view(['POST'])\n@permission_classes((IsAuthenticated, ))\n@authentication_classes([SessionAuthentication, TastyTokenAuthentication])\ndef add_project_to_program(request, program_pk):\n    program = get_object_or_404(Project, pk=program_pk)\n    parent_pk = request.data.get('parent')\n    if parent_pk is not None:\n        program = get_object_or_404(Project, pk=parent_pk)\n\n    project = Project.objects.create()\n    Project.new_project_created(project.id, request.user)   # Log creation\n    project.add_to_program(program)\n    # Set user's primary org as accountable partner\n    org = request.user.first_organisation()\n    if org is not None and org != program.reporting_org:\n        project.set_accountable_partner(org)\n    response = ProjectSerializer(project, context=dict(request=request)).data\n    return Response(response, status=HTTP_201_CREATED)\n\n\n@api_view(['GET'])\ndef project_title(request, project_pk):\n    project = get_object_or_404(Project, pk=project_pk)\n    data = {\n        'title': project.title,\n        'publishing_status': project.publishingstatus.status,\n        'has_hierarchy': project.parents_all().exists() or project.is_hierarchy_root(),\n        'pending_update_count': IndicatorPeriodData.objects.filter(\n            period__indicator__result__project=project,\n            status=IndicatorPeriodData.STATUS_PENDING_CODE\n        ).count()\n    }\n    return Response(data)\n", "path": "akvo/rest/views/project.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n\"\"\"Akvo RSR is covered by the GNU Affero General Public License.\n\nSee more details in the license.txt file located at the root folder of the Akvo RSR module.\nFor additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.\n\"\"\"\n\nfrom django.db.models import Q\nfrom django.shortcuts import get_object_or_404\nfrom django.http import Http404\nfrom rest_framework.authentication import SessionAuthentication\nfrom rest_framework.decorators import api_view, authentication_classes, permission_classes\nfrom rest_framework.permissions import IsAuthenticated\nfrom rest_framework.response import Response\nfrom rest_framework.status import HTTP_201_CREATED\nfrom geojson import Feature, Point, FeatureCollection\n\nfrom akvo.codelists.store.default_codelists import SECTOR_CATEGORY\nfrom akvo.rest.cache import serialized_project\nfrom akvo.rest.serializers import (ProjectSerializer, ProjectExtraSerializer,\n                                   ProjectExtraDeepSerializer,\n                                   ProjectIatiExportSerializer,\n                                   ProjectUpSerializer,\n                                   TypeaheadOrganisationSerializer,\n                                   ProjectMetadataSerializer,\n                                   OrganisationCustomFieldSerializer,\n                                   ProjectHierarchyRootSerializer,\n                                   ProjectHierarchyTreeSerializer,)\nfrom akvo.rest.models import TastyTokenAuthentication\nfrom akvo.rsr.models import Project, OrganisationCustomField, IndicatorPeriodData\nfrom akvo.rsr.views.my_rsr import user_viewable_projects\nfrom akvo.utils import codelist_choices, single_period_dates\nfrom ..viewsets import PublicProjectViewSet, ReadOnlyPublicProjectViewSet\n\n\nclass ProjectViewSet(PublicProjectViewSet):\n\n    \"\"\"\n    Viewset providing Project data.\n    \"\"\"\n    queryset = Project.objects.prefetch_related(\n        'publishingstatus',\n        'categories',\n        'keywords',\n        'partners',\n    )\n    serializer_class = ProjectSerializer\n    project_relation = ''\n\n    def get_queryset(self):\n        \"\"\"\n        Allow custom filter for sync_owner, since this field has been replaced by the\n        reporting org partnership.\n        \"\"\"\n\n        sync_owner = self.request.query_params.get('sync_owner', None)\n        reporting_org = self.request.query_params.get('reporting_org', None)\n\n        reporting_org = reporting_org or sync_owner\n        if reporting_org:\n            self.queryset = self.queryset.filter(\n                partnerships__iati_organisation_role=101,\n                partnerships__organisation__pk=reporting_org\n            ).distinct()\n        return super(ProjectViewSet, self).get_queryset()\n\n\nclass MyProjectsViewSet(PublicProjectViewSet):\n    \"\"\"Viewset providing listing of projects a user can edit.\"\"\"\n    queryset = Project.objects.all().select_related('publishingstatus')\\\n        .prefetch_related('locations', 'categories', 'related_projects', 'recipient_countries')\n    serializer_class = ProjectMetadataSerializer\n    project_relation = ''\n\n    def get_queryset(self):\n        if self.request.user.is_anonymous:\n            return Project.objects.none()\n        show_restricted = bool(self.request.query_params.get('show_restricted'))\n\n        filter_program = self.request.query_params.get('filter_program')\n        if filter_program:\n            try:\n                filter_program = int(filter_program)\n            except ValueError:\n                filter_program = None\n\n        queryset = user_viewable_projects(self.request.user, show_restricted, filter_program)\n\n        sector = self.request.query_params.get('sector', None)\n        if sector:\n            queryset = queryset.filter(sectors__sector_code=sector)\n        country = self.request.query_params.get('country', None)\n        if country:\n            queryset = queryset.filter(locations__country__iso_code=country)\\\n                .union(queryset.filter(recipient_countries__country__iexact=country))\n        return queryset\n\n\nclass ProjectHierarchyViewSet(ReadOnlyPublicProjectViewSet):\n    queryset = Project.objects.none()\n    serializer_class = ProjectHierarchyRootSerializer\n    project_relation = ''\n\n    def get_queryset(self):\n        if self.request.user.is_anonymous:\n            return Project.objects.none()\n        queryset = self.request.user.my_projects()\\\n                                    .published()\\\n                                    .filter(projecthierarchy__isnull=False)\n        return queryset\n\n    def retrieve(self, request, *args, **kwargs):\n        project = get_object_or_404(Project, pk=self.kwargs['pk'])\n        root = project.ancestor()\n        if not self.request.user.has_perm('rsr.view_project', root):\n            raise Http404\n\n        serializer = ProjectHierarchyTreeSerializer(root, context=self.get_serializer_context())\n\n        return Response(serializer.data)\n\n\nclass ProjectIatiExportViewSet(PublicProjectViewSet):\n    \"\"\"Lean viewset for project data, as used in the My IATI section of RSR.\"\"\"\n    queryset = Project.objects.only(\n        'id',\n        'title',\n        'is_public',\n        'status',\n        'run_iati_checks',\n    ).prefetch_related(\n        'partners',\n        'iati_checks',\n        'publishingstatus',\n        'partnerships',\n    )\n    serializer_class = ProjectIatiExportSerializer\n    project_relation = ''\n    paginate_by_param = 'limit'\n    max_paginate_by = 50\n\n    def get_queryset(self):\n        \"\"\"\n        Allow custom filter for sync_owner, since this field has been replaced by the\n        reporting org partnership.\n        \"\"\"\n        reporting_org = self.request.query_params.get('reporting_org', None)\n        if reporting_org:\n            self.queryset = self.queryset.filter(\n                partnerships__iati_organisation_role=101,\n                partnerships__organisation__pk=reporting_org\n            ).distinct()\n        return super(ProjectIatiExportViewSet, self).get_queryset()\n\n    def list(self, request, *args, **kwargs):\n        projects = self.queryset.filter(run_iati_checks=True)\n        for project in projects:\n            project.update_iati_checks()\n        return super(ProjectIatiExportViewSet, self).list(request, *args, **kwargs)\n\n\nclass ProjectExtraViewSet(ProjectViewSet):\n    r\"\"\"\n    Viewset providing extra Project data.\n\n    Allowed parameters are:\n    __limit__ (default 10, max 30),\n    __partnerships\\__organisation__ (filter on organisation ID), and\n    __publishingstatus\\__status__ (filter on publishing status)\n    \"\"\"\n\n    queryset = Project.objects.prefetch_related(\n        'publishingstatus',\n        'sectors',\n        'partnerships',\n        'budget_items',\n        'legacy_data',\n        'links',\n        'locations',\n        'locations__country',\n        'planned_disbursements',\n        'policy_markers',\n        'documents',\n        'conditions',\n        'contacts',\n        'project_updates',\n        'recipient_countries',\n        'recipient_regions',\n        'related_projects',\n        'results',\n        'sectors',\n        'transactions',\n    )\n    serializer_class = ProjectExtraSerializer\n    paginate_by_param = 'limit'\n    paginate_by = 10\n    max_paginate_by = 30\n\n\nclass ProjectExtraDeepViewSet(ProjectViewSet):\n    r\"\"\"\n    Viewset providing extra deep (depth=2 or bigger) Project data.\n\n    Allowed parameters are:\n    __limit__ (default 5, max 10),\n    __partnerships\\__organisation__ (filter on organisation ID), and\n    __publishingstatus\\__status__ (filter on publishing status)\n    \"\"\"\n\n    queryset = Project.objects.prefetch_related(\n        'publishingstatus',\n        'sectors',\n        'partnerships',\n        'budget_items',\n        'legacy_data',\n        'links',\n        'locations',\n        'locations__country',\n        'planned_disbursements',\n        'policy_markers',\n        'documents',\n        'conditions',\n        'contacts',\n        'project_updates',\n        'recipient_countries',\n        'recipient_regions',\n        'related_projects',\n        'results',\n        'sectors',\n        'transactions',\n    )\n    serializer_class = ProjectExtraDeepSerializer\n    paginate_by_param = 'limit'\n    paginate_by = 5\n    max_paginate_by = 10\n\n\nclass ProjectUpViewSet(ProjectViewSet):\n    r\"\"\"\n    Viewset providing extra data and limited filtering for Up in one go.\n\n    Allowed parameters are:\n    __limit__ (default 30, max 100),\n    __partnerships\\__organisation__ (filter on organisation ID), and\n    __publishingstatus\\__status__ (filter on publishing status)\n    \"\"\"\n\n    queryset = Project.objects.select_related(\n        'primary_location',\n    ).prefetch_related(\n        'publishingstatus',\n        'project_updates',\n        'categories',\n        'keywords',\n        'partners',\n    )\n    serializer_class = ProjectUpSerializer\n    paginate_by_param = 'limit'\n    max_paginate_by = 100\n\n\n###############################################################################\n# Project directory\n###############################################################################\n\n@api_view(['GET'])\ndef project_directory(request):\n    \"\"\"Return the values for various project filters.\n\n    Based on the current filters, it returns new options for all the (other)\n    filters. This is used to generate dynamic filters.\n\n    \"\"\"\n\n    page = request.rsr_page\n    projects = _project_list(request)\n    projects_data = [\n        serialized_project(project_id) for project_id in projects.values_list('pk', flat=True)\n    ]\n    organisations = list(projects.all_partners().values('id', 'name', 'long_name'))\n    organisations = TypeaheadOrganisationSerializer(organisations, many=True).data\n\n    custom_fields = (\n        OrganisationCustomField.objects.filter(type='dropdown',\n                                               organisation=page.organisation,\n                                               show_in_searchbar=True).order_by('order', 'id')\n        if page else []\n    )\n    sectors = [{'id': id_, 'name': name} for id_, name in codelist_choices(SECTOR_CATEGORY)]\n    response = {\n        'projects': projects_data,\n        'organisation': organisations,\n        'sector': sectors,\n        'custom_fields': OrganisationCustomFieldSerializer(custom_fields, many=True).data,\n    }\n\n    return Response(response)\n\n\ndef _project_list(request):\n    \"\"\"Return a project queryset based on the request\"\"\"\n    # Fetch projects based on whether we are an Akvo site or RSR main site\n    page = request.rsr_page\n    projects = page.projects() if page else Project.objects.all().public().published()\n\n    if not page:\n        # Exclude projects which don't have an image or a title for RSR site\n        projects = projects.exclude(Q(title='') | Q(current_image=''))\n    else:\n        # On partner sites, all projects show up. Partners are expected to fix\n        # their data to fix their pages!\n        pass\n    return projects\n\n\n@api_view(['GET'])\ndef project_location_geojson(request):\n    \"\"\"Return a GeoJSON with all the project locations.\"\"\"\n    projects = _project_list(request).prefetch_related('locations')\n    features = [\n        Feature(geometry=Point((location.longitude, location.latitude)),\n                properties=dict(\n                    project_title=project.title,\n                    project_subtitle=project.subtitle,\n                    project_url=request.build_absolute_uri(project.get_absolute_url()),\n                    project_id=project.pk,\n                    name=location.name,\n                    description=location.description))\n        for project in projects\n        for location in project.locations.all()\n        if location.is_valid()\n    ]\n    response = FeatureCollection(features)\n    return Response(response)\n\n\n@api_view(['POST'])\n@permission_classes((IsAuthenticated, ))\n@authentication_classes([SessionAuthentication, TastyTokenAuthentication])\ndef add_project_to_program(request, program_pk):\n    program = get_object_or_404(Project, pk=program_pk)\n    parent_pk = request.data.get('parent')\n    if parent_pk is not None:\n        program = get_object_or_404(Project, pk=parent_pk)\n\n    project = Project.objects.create()\n    Project.new_project_created(project.id, request.user)   # Log creation\n    project.add_to_program(program)\n    # Set user's primary org as accountable partner\n    org = request.user.first_organisation()\n    if org is not None and org != program.reporting_org:\n        project.set_accountable_partner(org)\n    response = ProjectSerializer(project, context=dict(request=request)).data\n    return Response(response, status=HTTP_201_CREATED)\n\n\n@api_view(['GET'])\ndef project_title(request, project_pk):\n    project = get_object_or_404(Project, pk=project_pk)\n    hierarchy_name = project.uses_single_indicator_period()\n    needs_reporting_timeout_days, _, _ = single_period_dates(hierarchy_name) if hierarchy_name else (None, None, None)\n\n    data = {\n        'title': project.title,\n        'publishing_status': project.publishingstatus.status,\n        'has_hierarchy': project.parents_all().exists() or project.is_hierarchy_root(),\n        'pending_update_count': IndicatorPeriodData.objects.filter(\n            period__indicator__result__project=project,\n            status=IndicatorPeriodData.STATUS_PENDING_CODE\n        ).count(),\n        'needs_reporting_timeout_days': needs_reporting_timeout_days,\n    }\n    return Response(data)\n", "path": "akvo/rest/views/project.py"}]}