Repository: akvo/akvo-rsr

Issue:
Feature Request: IATI profile url
### What are you trying to do?

Provide link to project's IATI profilea profile

### Describe the solution you'd like

Add a method to get the URL to the Project model

### Have you consider alternatives?

_No response_

### Additional context

_No response_


Discussion:

Resolving patch:
diff --git a/akvo/rest/serializers/project.py b/akvo/rest/serializers/project.py
--- a/akvo/rest/serializers/project.py
+++ b/akvo/rest/serializers/project.py
@@ -74,6 +74,7 @@ class ProjectSerializer(BaseRSRSerializer):
     can_edit_enumerator_access = serializers.SerializerMethodField()
     program = serializers.SerializerMethodField()
     targets_at = TargetsAtField(choices=Project.TARGETS_AT_OPTION, required=False)
+    iati_profile_url = serializers.SerializerMethodField()
 
     class Meta:
         model = Project
@@ -123,6 +124,9 @@ def get_program(self, obj):
             return None
         return {'id': program.id, 'title': program.title}
 
+    def get_iati_profile_url(self, obj):
+        return obj.get_iati_profile_url()
+
 
 class ProjectDirectorySerializer(serializers.ModelSerializer):
 
diff --git a/akvo/rsr/models/project.py b/akvo/rsr/models/project.py
--- a/akvo/rsr/models/project.py
+++ b/akvo/rsr/models/project.py
@@ -561,6 +561,15 @@ def cacheable_url(self):
         # Language names are 2 chars long
         return self.get_absolute_url()[3:]
 
+    def get_iati_profile_url(self):
+        if not self.iati_project_exports.filter(status=2).count():
+            return None
+        if not self.primary_organisation or not self.primary_organisation.iati_org_id:
+            return None
+        if not self.iati_activity_id:
+            return None
+        return f"https://d-portal.org/ctrack.html?reporting_ref={self.primary_organisation.iati_org_id}#view=act&aid={self.iati_activity_id}"
+
     @cached_property
     def is_unep_project(self):
         return 'UNEP Marine Litter Stocktake' in self.keyword_labels()


Test patch:
diff --git a/akvo/rsr/tests/models/test_project.py b/akvo/rsr/tests/models/test_project.py
--- a/akvo/rsr/tests/models/test_project.py
+++ b/akvo/rsr/tests/models/test_project.py
@@ -10,7 +10,7 @@
 
 from akvo.rsr.tests.base import BaseTestCase
 from akvo.rsr.models import BudgetItem, Partnership, Project, ProjectUpdate, Organisation, \
-    OrganisationIndicatorLabel, RelatedProject, OrganisationCodelist
+    OrganisationIndicatorLabel, RelatedProject, OrganisationCodelist, IatiExport
 
 
 class ProjectModelTestCase(BaseTestCase):
@@ -252,3 +252,59 @@ def test_project_ancestor(self):
         self.assertEqual(ancestor_p4, self.project1)
         ancestor_p5 = self.project5.ancestor()
         self.assertEqual(ancestor_p5, self.project1)
+
+
+class IatiProfileUrlTestCase(BaseTestCase):
+    def setUp(self):
+        super().setUp()
+        self.org = self.create_organisation('Acme')
+        self.project = self.create_project('Test project')
+        self.user = self.create_user('test@akvo.org', 'password')
+        self.iati_org_id = 'AB-1234'
+        self.iati_activity_id = 'CD-5678'
+        self.make_partner(self.project, self.org, Partnership.IATI_REPORTING_ORGANISATION)
+
+    def set_iati_org_id(self):
+        self.org.iati_org_id = self.iati_org_id
+        self.org.save(update_fields=['iati_org_id'])
+
+    def set_iati_activity_id(self):
+        self.project.iati_activity_id = self.iati_activity_id
+        self.project.save(update_fields=['iati_activity_id'])
+
+    def export_project(self):
+        iati_export = IatiExport.objects.create(reporting_organisation=self.org, user=self.user)
+        iati_export.projects.add(self.project)
+        iati_export.create_iati_file()
+
+    def test_no_export(self):
+        self.set_iati_org_id()
+        self.set_iati_activity_id()
+
+        project = Project.objects.get(id=self.project.id)
+        self.assertIsNone(project.get_iati_profile_url())
+
+    def test_no_iati_org_id(self):
+        self.set_iati_activity_id()
+        self.export_project()
+
+        project = Project.objects.get(id=self.project.id)
+        self.assertIsNone(project.get_iati_profile_url())
+
+    def test_no_iati_activity_id(self):
+        self.set_iati_org_id()
+        self.export_project()
+
+        project = Project.objects.get(id=self.project.id)
+        self.assertIsNone(project.get_iati_profile_url())
+
+    def test_iati_profile_url(self):
+        self.set_iati_org_id()
+        self.set_iati_activity_id()
+        self.export_project()
+
+        project = Project.objects.get(id=self.project.id)
+        self.assertEqual(
+            project.get_iati_profile_url(),
+            f"https://d-portal.org/ctrack.html?reporting_ref={self.iati_org_id}#view=act&aid={self.iati_activity_id}"
+        )