Repository: akvo/akvo-rsr

Issue:
Adding new dimension name to indicator should propagate to children
Adding dimension names to indicators after an import should update the child indicators and add the corresponding indicator dimension names to the children too. 


Discussion:

Resolving patch:
diff --git a/akvo/rsr/models/result/indicator.py b/akvo/rsr/models/result/indicator.py
--- a/akvo/rsr/models/result/indicator.py
+++ b/akvo/rsr/models/result/indicator.py
@@ -12,7 +12,7 @@
 from django.core.exceptions import ValidationError
 from django.db import models
 from django.utils.translation import ugettext_lazy as _
-from django.db.models.signals import post_save
+from django.db.models.signals import post_save, m2m_changed
 from django.dispatch import receiver
 
 from .indicator_period import IndicatorPeriod
@@ -227,3 +227,25 @@ def add_default_periods(sender, instance, created, **kwargs):
 
                 period.indicator_id = instance.id
                 period.save()
+
+
+@receiver(m2m_changed, sender=Indicator.dimension_names.through)
+def add_dimension_names_to_children(sender, instance, action, **kwargs):
+    if not action.startswith('post_'):
+        return
+
+    if not instance.child_indicators.exists():
+        return
+
+    dimension_name = kwargs['model'].objects.filter(id__in=kwargs['pk_set']).first()
+    for indicator in instance.child_indicators.all():
+        child_dimension_name, _ = dimension_name.child_dimension_names.get_or_create(
+            name=dimension_name.name,
+            parent_dimension_name=dimension_name,
+            project=indicator.result.project)
+
+        if action == 'post_add':
+            indicator.dimension_names.add(child_dimension_name)
+
+        elif action == 'post_remove':
+            indicator.dimension_names.remove(child_dimension_name)


Test patch:
diff --git a/akvo/rsr/tests/models/test_results_aggregation.py b/akvo/rsr/tests/models/test_results_aggregation.py
--- a/akvo/rsr/tests/models/test_results_aggregation.py
+++ b/akvo/rsr/tests/models/test_results_aggregation.py
@@ -110,7 +110,7 @@ class PeriodDisaggregationTestCase(TestCase):
 
     def setUp(self):
         # Setup a project with results framework and a user
-        self.user = User.objects.create(username='user1@com.com', email='user1@com.com')
+        self.user, _ = User.objects.get_or_create(username='user1@com.com', email='user1@com.com')
         self.project = Project.objects.create(title="Test project 1")
         self.result = Result.objects.create(project=self.project, title='Test Result')
         self.indicator = Indicator.objects.create(result=self.result, title='Test Indicator')
diff --git a/akvo/rsr/tests/results_framework/test_results_framework.py b/akvo/rsr/tests/results_framework/test_results_framework.py
--- a/akvo/rsr/tests/results_framework/test_results_framework.py
+++ b/akvo/rsr/tests/results_framework/test_results_framework.py
@@ -875,3 +875,24 @@ def test_allow_changing_parents_if_results_not_imported(self):
 
         # Then
         self.assertEqual(self.child_project.parents_all().first().id, project.id)
+
+    def test_adding_or_removing_indicator_dimension_names_changes_children(self):
+        # Given
+        indicator = self.indicator
+        dimension_name = self.dimension_name
+        child_indicator = Indicator.objects.get(
+            parent_indicator=indicator, result__project=self.child_project)
+        self.assertEqual(child_indicator.dimension_names.count(), 1)
+
+        # When
+        indicator.dimension_names.remove(dimension_name)
+
+        # Then
+        self.assertEqual(child_indicator.dimension_names.count(), 0)
+
+        # Adding back the dimension_name should propagate to children
+        # When
+        indicator.dimension_names.add(dimension_name)
+
+        # Then
+        self.assertEqual(child_indicator.dimension_names.count(), 1)