Repository: carpentries/amy

Issue:
Searching for duplication in the database
We need a page that will display pairs of people where:
1.  (A.firstname == B.lastname) and (B.lastname == A.firstname), i.e., we got someone's names flipped during bulk upload.
2.  (A.firstname == B.firstname) and (A.lastname == B.lastname) and (A.email != B.email), i.e., we may have two records for the same person because they changed email address.



Discussion:

Resolving patch:
diff --git a/workshops/templatetags/assignments.py b/workshops/templatetags/assignments.py
new file mode 100644
--- /dev/null
+++ b/workshops/templatetags/assignments.py
@@ -0,0 +1,13 @@
+from django import template
+from django.utils.safestring import mark_safe
+
+register = template.Library()
+
+
+@register.simple_tag
+def assign(variable):
+    """Directly pass a variable to the output.
+
+    This tag is intended to use with the new 'as' syntax:
+    {% assign "123" as number %}"""
+    return mark_safe(variable)
diff --git a/workshops/urls.py b/workshops/urls.py
--- a/workshops/urls.py
+++ b/workshops/urls.py
@@ -81,6 +81,7 @@
     url(r'^reports/all_activity_over_time/?$', views.all_activity_over_time, name='all_activity_over_time'),
     url(r'^reports/workshop_issues/?$', views.workshop_issues, name='workshop_issues'),
     url(r'^reports/instructor_issues/?$', views.instructor_issues, name='instructor_issues'),
+    url(r'^reports/duplicates/?$', views.duplicates, name='duplicates'),
 
     url(r'^revision/(?P<revision_id>[\d]+)/?$', views.object_changes, name='object_changes'),
 
diff --git a/workshops/views.py b/workshops/views.py
--- a/workshops/views.py
+++ b/workshops/views.py
@@ -2638,3 +2638,47 @@ def todo_delete(request, todo_id):
                      extra_tags='todos')
 
     return redirect(event_details, event_ident)
+
+
+# ------------------------------------------------------------
+
+@login_required
+def duplicates(request):
+    """Find possible duplicates amongst persons.
+
+    Criteria for persons:
+    * switched personal/family names
+    * same name on different people."""
+    names_normal = set(Person.objects.all().values_list('personal', 'family'))
+    names_switched = set(Person.objects.all().values_list('family',
+                                                          'personal'))
+    names = names_normal & names_switched  # intersection
+
+    switched_criteria = Q()
+    for personal, family in names:
+        # get people who appear in `names`
+        switched_criteria |= (Q(personal=personal) & Q(family=family))
+
+    switched_persons = Person.objects.filter(switched_criteria) \
+                                     .order_by('email')
+
+    duplicate_names = Person.objects.values('personal', 'family') \
+                                    .order_by() \
+                                    .annotate(count_id=Count('id')) \
+                                    .filter(count_id__gt=1)
+
+    duplicate_criteria = Q()
+    for name in duplicate_names:
+        # get people who appear in `names`
+        duplicate_criteria |= (Q(personal=name['personal']) &
+                               Q(family=name['family']))
+    duplicate_persons = Person.objects.filter(duplicate_criteria) \
+                                      .order_by('family', 'personal', 'email')
+
+    context = {
+        'title': 'Possible duplicates',
+        'switched_persons': switched_persons,
+        'duplicate_persons': duplicate_persons,
+    }
+
+    return render(request, 'workshops/duplicates.html', context)


Test patch:
diff --git a/workshops/test/test_finding_duplicates.py b/workshops/test/test_finding_duplicates.py
new file mode 100644
--- /dev/null
+++ b/workshops/test/test_finding_duplicates.py
@@ -0,0 +1,42 @@
+from django.core.urlresolvers import reverse
+
+from ..models import Person
+from .base import TestBase
+
+
+class TestFindingDuplicates(TestBase):
+    def setUp(self):
+        self._setUpUsersAndLogin()
+
+        self.harry = Person.objects.create(
+            personal='Harry', family='Potter', username='potter_harry',
+            email='hp@hogwart.edu')
+        self.potter = Person.objects.create(
+            personal='Potter', family='Harry', username='harry_potter',
+            email='hp+1@hogwart.edu')
+        self.ron = Person.objects.create(
+            personal='Ron', family='Weasley', username='weasley_ron',
+            email='rw@hogwart.edu')
+        self.ron2 = Person.objects.create(
+            personal='Ron', family='Weasley', username='weasley_ron_2',
+            email='rw+1@hogwart.edu')
+
+        self.url = reverse('duplicates')
+
+    def test_switched_names_persons(self):
+        rv = self.client.get(self.url)
+        switched = rv.context['switched_persons']
+        self.assertIn(self.harry, switched)
+        self.assertIn(self.potter, switched)
+        self.assertNotIn(self.ron, switched)
+        self.assertNotIn(self.ron2, switched)
+
+    def test_duplicate_persons(self):
+        rv = self.client.get(self.url)
+        switched = rv.context['duplicate_persons']
+        self.assertIn(self.ron, switched)
+        self.assertIn(self.ron2, switched)
+        self.assertNotIn(self.harry, switched)
+        self.assertNotIn(self.potter, switched)
+
+    # there might be more to come