Repository: akvo/akvo-rsr

Issue:
File type bug in project editor
In section 9, Links and documents, in the project editor the Document format list (shown when the IATI validation set is used) displays the wrong field of the underlying code list, FILE_FORMAT.

![doc_type](https://user-images.githubusercontent.com/142613/31770993-a4988c18-b4da-11e7-8cea-517d4e5ee3d2.png)

The **code** field should be used rather than the **category**.


Discussion:
![image](https://user-images.githubusercontent.com/315678/31875048-fed674bc-b7e9-11e7-884a-6faf82ec9cfb.png)

Unable to reproduce this in my Chrome/FF browser. Could this be a browser specific issue?
Unable to reproduce it in the latest Safari, either. 
![image](https://user-images.githubusercontent.com/315678/31875166-9549fcf2-b7ea-11e7-899c-bcd633de2133.png)


Resolving patch:
diff --git a/akvo/utils.py b/akvo/utils.py
--- a/akvo/utils.py
+++ b/akvo/utils.py
@@ -284,16 +284,29 @@ def codelist_choices(codelist, show_code=True):
     :param show_code: Show the code (e.g. '1 - ..') in front of the name, True by default
     :return: List of tuples with available choices, tuples in the form of (code, name)
     """
-    name_index = 0
-    for index, header in enumerate(codelist[0]):
-        if header == 'name':
-            name_index = index
-            break
-
-    if name_index > 0 and show_code:
-        return [(cl[0], '%s - %s' % (cl[0], cl[name_index])) for cl in codelist[1:]]
+
+    fields = codelist[0]
+
+    try:
+        name_index = fields.index('name')
+    except:
+        name_index = None
+
+    # the code field has to exist or we're in trouble
+    code_index = fields.index('code')
+
+    list_items = codelist[1:]
+
+    if name_index is not None and show_code:
+        return [
+            (item[code_index], u'{} - {}'.format(item[code_index], item[name_index]))
+            for item in list_items
+        ]
     else:
-        return [(cl[0], cl[name_index]) for cl in codelist[1:]]
+        return [
+            (item[code_index], item[name_index] if name_index is not None else item[code_index])
+            for item in list_items
+        ]
 
 
 def codelist_value(model, instance, field, version=settings.IATI_VERSION):


Test patch:
diff --git a/akvo/rsr/tests/rsr_utils/test_utils.py b/akvo/rsr/tests/rsr_utils/test_utils.py
--- a/akvo/rsr/tests/rsr_utils/test_utils.py
+++ b/akvo/rsr/tests/rsr_utils/test_utils.py
@@ -12,7 +12,8 @@
 from akvo.utils import (rsr_send_mail_to_users, model_and_instance_based_filename,
                         who_am_i, who_is_parent, to_gmt, rsr_show_keywords,
                         custom_get_or_create_country, right_now_in_akvo,
-                        pagination, filter_query_string, codelist_name, get_country)
+                        pagination, filter_query_string, codelist_name, get_country,
+                        codelist_choices)
 
 from django.core import mail
 from django.http.request import QueryDict
@@ -144,6 +145,95 @@ def test_codelist_name(self):
         name = codelist_name(CodelistSector, sector, 'sector_code', version='16')
         self.assertEqual(name, '140')
 
+    def test_codelist_choices_true(self):
+        """
+        Test calling codelist_choices(<codelist>, True)
+        """
+        # snippet of FINANCE_TYPE codelist
+        codelist_1 = (
+            (u"code", u"name", u"description"),
+            (u"1", u"Global", u"The activity scope is global"),
+            (u"2", u"Regional", u"The activity scope is a supranational region"),
+            (u"3", u"Multi-national",
+             u"The activity scope covers multiple countries, that don't constitute a region"),
+            (u"4", u"National", u"The activity scope covers one country"),
+        )
+        choices_with_code_1 = [
+            (u"1", u"1 - Global",),
+            (u"2", u"2 - Regional",),
+            (u"3", u"3 - Multi-national",),
+            (u"4", u"4 - National",),
+        ]
+        codelist_2 = (
+            (u"category", u"code", u"name"),
+            (u"100", u"110", u"Standard grant"),
+            (u"100", u"111", u"Subsidies to national private investors"),
+            (u"100", u"210", u"Interest subsidy"),
+            (u"200", u"211", u"Interest subsidy to national private exporters"),
+        )
+        choices_with_code_2 = [
+            (u"110", u"110 - Standard grant"),
+            (u"111", u"111 - Subsidies to national private investors"),
+            (u"210", u"210 - Interest subsidy"),
+            (u"211", u"211 - Interest subsidy to national private exporters"),
+        ]
+        generated_choices_with_code_1 = codelist_choices(codelist_1)
+        self.assertEqual(choices_with_code_1, generated_choices_with_code_1)
+        generated_choices_with_code_2 = codelist_choices(codelist_2)
+        self.assertEqual(choices_with_code_2, generated_choices_with_code_2)
+
+    def test_codelist_choices_false(self):
+        """
+        Test calling codelist_choices(<codelist>, False)
+        """
+        # snippet of FINANCE_TYPE codelist
+        codelist_1 = (
+            (u"code", u"name", u"description"),
+            (u"1", u"Global", u"The activity scope is global"),
+            (u"2", u"Regional", u"The activity scope is a supranational region"),
+            (u"3", u"Multi-national",
+             u"The activity scope covers multiple countries, that don't constitute a region"),
+            (u"4", u"National", u"The activity scope covers one country"),
+        )
+        choices_1 = [
+            (u"1", u"Global",),
+            (u"2", u"Regional",),
+            (u"3", u"Multi-national",),
+            (u"4", u"National",),
+        ]
+        codelist_2 = (
+            (u"category", u"code", u"name"),
+            (u"100", u"110", u"Standard grant"),
+            (u"100", u"111", u"Subsidies to national private investors"),
+            (u"100", u"210", u"Interest subsidy"),
+            (u"200", u"211", u"Interest subsidy to national private exporters"),
+        )
+        choices_2 = [
+            (u"110", u"Standard grant"),
+            (u"111", u"Subsidies to national private investors"),
+            (u"210", u"Interest subsidy"),
+            (u"211", u"Interest subsidy to national private exporters"),
+        ]
+        codelist_3 = (
+            (u"category", u"code"),
+            (u"application", u"application/1d-interleaved-parityfec"),
+            (u"application", u"application/3gpdash-qoe-report+xml"),
+            (u"application", u"application/3gpp-ims+xml"),
+            (u"application", u"application/A2L"),
+        )
+        choices_with_code_3 = [
+            (u"application/1d-interleaved-parityfec", u"application/1d-interleaved-parityfec"),
+            (u"application/3gpdash-qoe-report+xml", u"application/3gpdash-qoe-report+xml"),
+            (u"application/3gpp-ims+xml", u"application/3gpp-ims+xml"),
+            (u"application/A2L", u"application/A2L"),
+        ]
+        generated_choices_1 = codelist_choices(codelist_1, False)
+        self.assertEqual(choices_1, generated_choices_1)
+        generated_choices_2 = codelist_choices(codelist_2, False)
+        self.assertEqual(choices_2, generated_choices_2)
+        generated_choices_with_code_3 = codelist_choices(codelist_3)
+        self.assertEqual(choices_with_code_3, generated_choices_with_code_3)
+
     def test_countries(self):
         """Test retrieving country name from (lat, long)."""