Repository: chaoss/grimoirelab-sortinghat (language: python)

Issue:
Order individuals by number of identities
Users should be able to order the individuals based on the amount of identities they have. This would help identify snowball issues quicker.


Discussion / hints:

Fix patch:
diff --git a/releases/unreleased/order-individuals-by-identities.yml b/releases/unreleased/order-individuals-by-identities.yml
new file mode 100644
index 00000000..7f441868
--- /dev/null
+++ b/releases/unreleased/order-individuals-by-identities.yml
@@ -0,0 +1,8 @@
+---
+title: Order individuals by indentities
+category: added
+author: Eva Millán <evamillan@bitergia.com>
+issue: 732
+notes: |
+  Adds the option to order the individuals by the number
+  of identities they have.
diff --git a/sortinghat/core/schema.py b/sortinghat/core/schema.py
index 82e2c1be..89b9dd87 100644
--- a/sortinghat/core/schema.py
+++ b/sortinghat/core/schema.py
@@ -30,7 +30,7 @@
 from django.core.paginator import Paginator
 from django.db.models import Q, Subquery
 
-from django.db.models import JSONField
+from django.db.models import (JSONField, Count)
 
 from django_rq import enqueue
 
@@ -1392,7 +1392,7 @@ def resolve_individuals(self, info, filters=None,
                             page_size=settings.SORTINGHAT_API_PAGE_SIZE,
                             order_by='mk',
                             **kwargs):
-        query = Individual.objects.order_by(to_snake_case(order_by))
+        query = Individual.objects.annotate(identities_count=Count('identities')).order_by(to_snake_case(order_by))
 
         if filters and 'uuid' in filters:
             indv_uuid = filters['uuid']
diff --git a/ui/src/components/IndividualsTable.vue b/ui/src/components/IndividualsTable.vue
index f10fb2e7..db8d136f 100644
--- a/ui/src/components/IndividualsTable.vue
+++ b/ui/src/components/IndividualsTable.vue
@@ -56,6 +56,10 @@
             text: 'Name',
             value: 'profile__name',
           },
+          {
+            text: 'Identities',
+            value: 'identitiesCount',
+          },
         ]"
         :set-filters="setFilters"
       />


Test patch:
diff --git a/config/settings/testing.py b/config/settings/testing.py
index 0721cbdc..d4c78604 100644
--- a/config/settings/testing.py
+++ b/config/settings/testing.py
@@ -28,7 +28,6 @@
 ]
 
 SQL_MODE = [
-    'ONLY_FULL_GROUP_BY',
     'NO_ZERO_IN_DATE',
     'NO_ZERO_DATE',
     'ERROR_FOR_DIVISION_BY_ZERO',
diff --git a/tests/test_schema.py b/tests/test_schema.py
index 031576a4..2e2cb581 100644
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -3854,6 +3854,64 @@ def test_order_by_profile_name(self):
         indv = individuals[2]
         self.assertEqual(indv['mk'], indv1.mk)
 
+    def test_order_by_identities_count(self):
+        """Check whether it returns the individuals ordered by their number of identities"""
+
+        indv1 = Individual.objects.create(mk='a9b403e150dd4af8953a52a4bb841051e4b705d9')
+        Identity.objects.create(uuid='A001',
+                                name='John Doe',
+                                source='scm',
+                                individual=indv1)
+        indv2 = Individual.objects.create(mk='185c4b709e5446d250b4fde0e34b78a2b4fde0e3')
+        Identity.objects.create(uuid='B001',
+                                name='Jane Roe',
+                                source='scm',
+                                individual=indv2)
+        Identity.objects.create(uuid='B002',
+                                name='Jane Roe',
+                                source='mls',
+                                individual=indv2)
+        Identity.objects.create(uuid='B003',
+                                name='Jane Roe',
+                                source='alt',
+                                individual=indv2)
+        indv3 = Individual.objects.create(mk='c6d2504fde0e34b78a185c4b709e5442d045451c')
+        Identity.objects.create(uuid='C001',
+                                name='John Smith',
+                                source='scm',
+                                individual=indv3)
+        Identity.objects.create(uuid='C002',
+                                name='John Smith',
+                                source='mls',
+                                individual=indv3)
+
+        # Test ascending order
+        client = graphene.test.Client(schema)
+        executed = client.execute(SH_INDIVIDUALS_ORDER_BY % 'identitiesCount',
+                                  context_value=self.context_value)
+
+        individuals = executed['data']['individuals']['entities']
+
+        indv = individuals[0]
+        self.assertEqual(indv['mk'], indv1.mk)
+        indv = individuals[1]
+        self.assertEqual(indv['mk'], indv3.mk)
+        indv = individuals[2]
+        self.assertEqual(indv['mk'], indv2.mk)
+
+        # Test descending order
+        executed = client.execute(SH_INDIVIDUALS_ORDER_BY % '-identitiesCount',
+                                  context_value=self.context_value)
+
+        individuals = executed['data']['individuals']['entities']
+
+        indv = individuals[0]
+        self.assertEqual(indv['mk'], indv2.mk)
+        indv = individuals[1]
+        self.assertEqual(indv['mk'], indv3.mk)
+        indv = individuals[2]
+        self.assertEqual(indv['mk'], indv1.mk)
+
     def test_pagination(self):
         """Check whether it returns the individuals searched when using pagination"""
 
diff --git a/ui/tests/unit/__snapshots__/mutations.spec.js.snap b/ui/tests/unit/__snapshots__/mutations.spec.js.snap
index 403bf806..8041295c 100644
--- a/ui/tests/unit/__snapshots__/mutations.spec.js.snap
+++ b/ui/tests/unit/__snapshots__/mutations.spec.js.snap
@@ -84,7 +84,7 @@ exports[`IndividualsTable Mock query for deleteIdentity 1`] = `
     <search-stub
       class="search-box pa-0 flex-grow-1"
       filterselector="true"
-      orderoptions="[object Object],[object Object],[object Object]"
+      orderoptions="[object Object],[object Object],[object Object],[object Object]"
       orderselector="true"
       validfilters="[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]"
     />
@@ -380,7 +380,7 @@ exports[`IndividualsTable Mock query for merge 1`] = `
     <search-stub
       class="search-box pa-0 flex-grow-1"
       filterselector="true"
-      orderoptions="[object Object],[object Object],[object Object]"
+      orderoptions="[object Object],[object Object],[object Object],[object Object]"
       orderselector="true"
       validfilters="[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]"
     />
@@ -676,7 +676,7 @@ exports[`IndividualsTable Mock query for moveIdentity 1`] = `
     <search-stub
       class="search-box pa-0 flex-grow-1"
       filterselector="true"
-      orderoptions="[object Object],[object Object],[object Object]"
+      orderoptions="[object Object],[object Object],[object Object],[object Object]"
       orderselector="true"
       validfilters="[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]"
     />
@@ -972,7 +972,7 @@ exports[`IndividualsTable Mock query for unmerge 1`] = `
     <search-stub
       class="search-box pa-0 flex-grow-1"
       filterselector="true"
-      orderoptions="[object Object],[object Object],[object Object]"
+      orderoptions="[object Object],[object Object],[object Object],[object Object]"
       orderselector="true"
       validfilters="[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]"
     />
@@ -1268,7 +1268,7 @@ exports[`IndividualsTable Mock query for updateEnrollment 1`] = `
     <search-stub
       class="search-box pa-0 flex-grow-1"
       filterselector="true"
-      orderoptions="[object Object],[object Object],[object Object]"
+      orderoptions="[object Object],[object Object],[object Object],[object Object]"
       orderselector="true"
       validfilters="[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]"
     />
@@ -1564,7 +1564,7 @@ exports[`IndividualsTable Mock query for withdraw 1`] = `
     <search-stub
       class="search-box pa-0 flex-grow-1"
       filterselector="true"
-      orderoptions="[object Object],[object Object],[object Object]"
+      orderoptions="[object Object],[object Object],[object Object],[object Object]"
       orderselector="true"
       validfilters="[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]"
     />
diff --git a/ui/tests/unit/__snapshots__/queries.spec.js.snap b/ui/tests/unit/__snapshots__/queries.spec.js.snap
index 9bd7f916..d9973796 100644
--- a/ui/tests/unit/__snapshots__/queries.spec.js.snap
+++ b/ui/tests/unit/__snapshots__/queries.spec.js.snap
@@ -91,7 +91,7 @@ exports[`IndividualsTable Mock query for getCountries 1`] = `
     <search-stub
       class="search-box pa-0 flex-grow-1"
       filterselector="true"
-      orderoptions="[object Object],[object Object],[object Object]"
+      orderoptions="[object Object],[object Object],[object Object],[object Object]"
       orderselector="true"
       validfilters="[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]"
     />
@@ -387,7 +387,7 @@ exports[`IndividualsTable Mock query for getPaginatedIndividuals 1`] = `
     <search-stub
       class="search-box pa-0 flex-grow-1"
       filterselector="true"
-      orderoptions="[object Object],[object Object],[object Object]"
+      orderoptions="[object Object],[object Object],[object Object],[object Object]"
       orderselector="true"
       validfilters="[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]"
     />