Repository: akvo/akvo-rsr

Issue:
IATI export should report error when all indicators are qualitative
When exporting to IATI a project with only qualitative indicators should report this as an error. This is because the export won't include any indicators as only qualitative indicators are exported.


Discussion:

Resolving patch:
diff --git a/akvo/iati/checks/fields/results.py b/akvo/iati/checks/fields/results.py
--- a/akvo/iati/checks/fields/results.py
+++ b/akvo/iati/checks/fields/results.py
@@ -4,7 +4,7 @@
 # See more details in the license.txt file located at the root folder of the Akvo RSR module.
 # For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.
 
-from akvo.rsr.models.result.utils import QUALITATIVE
+from akvo.rsr.models.result.utils import QUANTITATIVE
 
 DGIS_VALIDATION_SET_NAME = u"DGIS IATI"
 
@@ -28,12 +28,14 @@ def results(project):
             all_checks_passed = False
             checks.append((u'error', u'result (id: %s) has no title specified' % str(result.pk)))
 
-        if not result.indicators.all():
+        if not result.indicators.filter(type=QUANTITATIVE):
             all_checks_passed = False
-            checks.append((u'error', u'result (id: %s) has no indicator(s)' % str(result.pk)))
+            checks.append(
+                (u'error', u'result (id: %s) has no quantitative indicator(s)' % str(result.pk))
+            )
 
-        for indicator in result.indicators.all():
-            if indicator.type != QUALITATIVE and not indicator.measure:
+        for indicator in result.indicators.filter(type=QUANTITATIVE):
+            if not indicator.measure:
                 all_checks_passed = False
                 checks.append((u'error', u'indicator (id: %s) has no measure specified' %
                                str(indicator.pk)))
diff --git a/akvo/iati/exports/iati_export.py b/akvo/iati/exports/iati_export.py
--- a/akvo/iati/exports/iati_export.py
+++ b/akvo/iati/exports/iati_export.py
@@ -114,6 +114,8 @@ def __init__(self, projects, version='2.02', iati_export=None, excluded_elements
         :param iati_export: IatiExport Django object
         :param excluded_elements: List of fieldnames that should be ignored when exporting
         """
+        from akvo.rsr.models import IatiExport
+
         self.projects = projects
         self.version = version
         self.iati_export = iati_export
@@ -139,5 +141,5 @@ def __init__(self, projects, version='2.02', iati_export=None, excluded_elements
             # Update IATI activity export's status to indicate that export has finished
             iati_activity_export = getattr(self, 'iati_activity_export', None)
             if iati_activity_export:
-                iati_activity_export.status = 2
+                iati_activity_export.status = IatiExport.STATUS_IN_PROGRESS
                 iati_activity_export.save(update_fields=['status'])
diff --git a/akvo/rsr/models/iati_export.py b/akvo/rsr/models/iati_export.py
--- a/akvo/rsr/models/iati_export.py
+++ b/akvo/rsr/models/iati_export.py
@@ -19,15 +19,18 @@ def file_path(self, filename):
     return 'db/organisation/%s/iati/%s' % (str(self.reporting_organisation.pk), filename)
 
 
-STATUS_CODE = {
-    1: _(u'pending'),
-    2: _(u'in progress'),
-    3: _(u'completed'),
-    4: _(u'cancelled')
-}
-
-
 class IatiExport(TimestampsMixin, models.Model):
+    STATUS_PENDING = 1
+    STATUS_IN_PROGRESS = 2
+    STATUS_COMPLETED = 3
+    STATUS_CANCELLED = 4
+    STATUS_CODE = {
+        STATUS_PENDING: _(u'pending'),
+        STATUS_IN_PROGRESS: _(u'in progress'),
+        STATUS_COMPLETED: _(u'completed'),
+        STATUS_CANCELLED: _(u'cancelled')
+    }
+
     reporting_organisation = models.ForeignKey(
         'Organisation', verbose_name=_(u'reporting organisation'), related_name='iati_exports'
     )
@@ -36,7 +39,7 @@ class IatiExport(TimestampsMixin, models.Model):
     )
     projects = models.ManyToManyField('Project', verbose_name=_(u'projects'))
     version = ValidXMLCharField(_(u'version'), max_length=4, default='2.02')
-    status = models.PositiveSmallIntegerField(_(u'status'), default=1)
+    status = models.PositiveSmallIntegerField(_(u'status'), default=STATUS_PENDING)
     iati_file = models.FileField(_(u'IATI file'), blank=True, upload_to=file_path)
     is_public = models.BooleanField(_(u'public'), default=True)
 
@@ -52,16 +55,16 @@ def __unicode__(self):
             return u'%s' % _(u'IATI export for unknown organisation')
 
     def show_status(self):
-        if self.status not in STATUS_CODE.keys():
+        if self.status not in self.STATUS_CODE.keys():
             return _(u'unknown status')
         else:
-            return STATUS_CODE[self.status].title()
+            return self.STATUS_CODE[self.status].title()
 
     def update_status(self, status_code):
         """
         Update the status of this IATI export.
 
-        :param status_code; Integer in STATUS_CODE keys
+        :param status_code; Integer in self.STATUS_CODE keys
         """
         self.status = status_code
         self.save(update_fields=['status'])
@@ -79,8 +82,7 @@ def create_iati_file(self):
         """
         Create an IATI XML file.
         """
-        # Set status to 'In progress'
-        self.update_status(2)
+        self.update_status(self.STATUS_IN_PROGRESS)
 
         # Retrieve all projects
         projects = self.projects.all()
@@ -93,18 +95,15 @@ def create_iati_file(self):
                     datetime.utcnow().strftime("%Y%m%d-%H%M%S") + '.xml')
                 )
 
-                # All done, so update the status to 'Completed'
-                self.update_status(3)
+                self.update_status(self.STATUS_COMPLETED)
             except:
-                # Something went wrong, so update the status to 'Cancelled'
-                self.update_status(4)
+                self.update_status(self.STATUS_CANCELLED)
         else:
-            # No projects, so update the status to 'Cancelled'
-            self.update_status(4)
+            self.update_status(self.STATUS_CANCELLED)
 
     def processed_projects(self):
         """
         Find the number of processed projects of this IATI export. Generally, for completed
         exports, this number will be the same as the number of total projects.
         """
-        return self.iati_activity_exports.filter(status=2).count()
+        return self.iati_activity_exports.filter(status=self.STATUS_IN_PROGRESS).count()


Test patch:
diff --git a/akvo/rsr/tests/iati_checks/test_iati_checks_fields_results.py b/akvo/rsr/tests/iati_checks/test_iati_checks_fields_results.py
--- a/akvo/rsr/tests/iati_checks/test_iati_checks_fields_results.py
+++ b/akvo/rsr/tests/iati_checks/test_iati_checks_fields_results.py
@@ -64,7 +64,7 @@ def test_iati_checks_fields_results_result_errors(self):
         self.assertEquals(len(checks), 3)
         self.assertIn(u'has no type specified', checks[0][1])
         self.assertIn(u'has no title specified', checks[1][1])
-        self.assertIn(u'has no indicator', checks[2][1])
+        self.assertIn(u'has no quantitative indicator', checks[2][1])
 
     def test_iati_checks_fields_results_indicator_error(self):
         # Given an indicator missing title, measure and baseline year and value