Repository: akvo/akvo-rsr

Issue:
Improve IATI export
## Test plan

Before starting with testing, make sure to perform two one-time actions:
- The `iati_export` cron job is running (done on Test and UAT)
  - See running cron jobs by running: `manage.py crontab show`;
- The `perform_iati_checks` management command has been run (done on Test and UAT).

---

GIVEN the 'My IATI' section in MyRSR
WHEN connected to multiple organisations (or as superuser)
THEN an organisation selection screen should be shown

WHEN connected to one organisation
THEN this organisation should be automatically selected

GIVEN the 'My IATI' section in MyRSR
WHEN an organisation has been selected
THEN the overview of all IATI exports should be shown
AND for each export the status, number of projects, created by, created at and IATI version should be shown
AND the latest IATI export should be shown in a green row
AND a pending or in progress IATI export should be shown in a yellow row
AND a cancelled or export without an IATI file should be shown in a red row

GIVEN the 'My IATI' section in MyRSR
WHEN an organisation has been selected
THEN it should be possible to select whether the latest IATI file is shown on the organisation page

GIVEN that is has been set that the latest IATI file is shown on the organisation page
THEN it should be shown on the organisation page as well
ELSE the IATI file should not be shown

GIVEN that the 'Add new IATI export' button is clicked
THEN the user should be redirected to the project selection overview

GIVEN the project selection overview
WHEN looking at the projects overview
THEN all projects where the selected organisation is reporting organisation should be shown

GIVEN the project selection overview
WHEN applying a filter
THEN the project selection should change
AND the indication of the number of projects selected should indicate the number of selected projects

GIVEN the project selection overview
WHEN projects are selected AND the 'create new IATI export' button is clicked
THEN the user should be redirected to the IATI exports overview
AND the top IATI export should be the new IATI export (with 'Pending' status)

GIVEN the IATI export overview
WHEN an export is pending or in progress
THEN the overview should be refreshed every 10 seconds
AND when an export is in progress, the number of processed projects should be shown
## Issue description

Currently, an IATI export with more than 70 projects will give a DB timeout. However, we need to be able to export an IATI file with any amount of projects. Similar to the IATI import, we can use a cron job for this.
- [x] Move IATI export to a cron job implementation
- [x] Update the 'My IATI' tab in MyRSR



Discussion:

Resolving patch:
diff --git a/akvo/rest/views/project_editor.py b/akvo/rest/views/project_editor.py
--- a/akvo/rest/views/project_editor.py
+++ b/akvo/rest/views/project_editor.py
@@ -558,10 +558,8 @@ def project_editor(request, pk=None):
             break
 
     # Update the IATI checks for every save in the editor.
-    try:
-        project.update_iati_checks()
-    except:
-        pass
+    updated_project = Project.objects.get(pk=pk)
+    updated_project.update_iati_checks()
 
     return Response(
         {
@@ -772,5 +770,8 @@ def log_project_addition(request, project_pk=None):
         change_message=message
     )
 
+    # Perform IATI checks after a project has been created.
+    project.update_iati_checks()
+
     content = {'log_entry': 'added successfully'}
     return Response(content, status=status.HTTP_201_CREATED)
diff --git a/akvo/rsr/models/__init__.py b/akvo/rsr/models/__init__.py
--- a/akvo/rsr/models/__init__.py
+++ b/akvo/rsr/models/__init__.py
@@ -319,6 +319,10 @@
 rules.add_perm('rsr.change_projectdocument', is_rsr_admin | is_org_admin | is_org_project_editor)
 rules.add_perm('rsr.delete_projectdocument', is_rsr_admin | is_org_admin | is_org_project_editor)
 
+rules.add_perm('rsr.add_iatiexport', is_rsr_admin | is_org_admin)
+rules.add_perm('rsr.change_iatiexport', is_rsr_admin | is_org_admin)
+rules.add_perm('rsr.delete_iatiexport', is_rsr_admin | is_org_admin)
+
 rules.add_perm('rsr.add_organisation', is_rsr_admin | is_org_admin | is_org_project_editor)
 rules.add_perm('rsr.change_organisation', is_rsr_admin | is_org_admin | is_org_project_editor)
 rules.add_perm('rsr.delete_organisation', is_rsr_admin)
diff --git a/akvo/rsr/permissions.py b/akvo/rsr/permissions.py
--- a/akvo/rsr/permissions.py
+++ b/akvo/rsr/permissions.py
@@ -9,7 +9,8 @@
 from django.contrib.auth import get_user_model
 from django.contrib.auth.models import Group
 
-from .models import Employment, Organisation, PartnerSite, Project, ProjectUpdate, PublishingStatus
+from .models import (Employment, IatiExport, Organisation, PartnerSite, Project, ProjectUpdate,
+                     PublishingStatus)
 
 
 @rules.predicate
@@ -25,24 +26,29 @@ def is_org_admin(user, obj):
         return False
     for employment in user.employers.approved():
         if employment.group == Group.objects.get(name='Admins'):
+            org = employment.organisation
             if not obj:
                 return True
             elif isinstance(obj, Organisation):
-                if obj in employment.organisation.content_owned_organisations():
+                if obj in org.content_owned_organisations():
                     return True
-            elif isinstance(obj, get_user_model()) and obj in employment.organisation.all_users():
+            elif isinstance(obj, get_user_model()) and obj in org.all_users():
                 return True
             elif type(obj) == Employment and \
-                    obj.organisation in employment.organisation.content_owned_organisations():
+                    obj.organisation in org.content_owned_organisations():
                 return True
-            elif isinstance(obj, Project) and obj in employment.organisation.all_projects():
+            elif isinstance(obj, Project) and obj in org.all_projects():
                 return True
-            elif isinstance(obj, PublishingStatus) and obj in employment.organisation.all_projects().publishingstatuses():
+            elif isinstance(obj, PublishingStatus) and \
+                    obj in org.all_projects().publishingstatuses():
                 return True
-            elif isinstance(obj, PartnerSite) and obj in employment.organisation.partnersites():
+            elif isinstance(obj, PartnerSite) and obj in org.partnersites():
                 return True
             elif isinstance(obj, ProjectUpdate) and obj.user == user:
                 return True
+            elif isinstance(obj, IatiExport) and \
+                    obj.reporting_organisation in org.content_owned_organisations():
+                return True
             else:
                 try:
                     if obj.project and obj.project in employment.organisation.all_projects():


Test patch: