Repository: carpentries/amy
Issue: Replace contains with icontains

We use `contains` instead of `icontains` in many places. On SQLite, [both matches are case-insensitively](https://docs.djangoproject.com/el/1.10/ref/databases/#substring-matching-and-case-sensitivity), but this will result in bugs when we switch to i.e. PostgreSQL.


Changed files:
workshops/views.py, pydata/views.py, workshops/test/test_search.py, workshops/migrations/0054_self_organized_host.py

diff --git a/pydata/views.py b/pydata/views.py
index f0c6fb75..33dd937c 100644
--- a/pydata/views.py
+++ b/pydata/views.py
@@ -66,7 +66,7 @@ class BaseImport(OnlyForAdminsMixin, View):
         try:
             url = request.GET['url']
             conf_url, pk = self.get_pk(url).groups()
-            event = Event.objects.get(url__contains=conf_url)
+            event = Event.objects.get(url__icontains=conf_url)
             client = self.client(event)
             obj = client[pk]
             return JsonResponse(self.serialize(obj))
diff --git a/workshops/migrations/0054_self_organized_host.py b/workshops/migrations/0054_self_organized_host.py
index 592b9276..8b90d764 100644
--- a/workshops/migrations/0054_self_organized_host.py
+++ b/workshops/migrations/0054_self_organized_host.py
@@ -24,8 +24,8 @@ def update_administrator_to_self_organized(apps, schema_editor):
     Event.objects.filter(administrator__isnull=True) \\
         .filter(
             Q(invoice_status='na-self-org') |
-            Q(notes__contains='self-organized') |
-            Q(notes__contains='self organized')
+            Q(notes__icontains='self-organized') |
+            Q(notes__icontains='self organized')
         ) \\
         .update(administrator=self_org)
 
diff --git a/workshops/test/test_search.py b/workshops/test/test_search.py
index a4cb4030..fd92d0b1 100644
--- a/workshops/test/test_search.py
+++ b/workshops/test/test_search.py
@@ -41,6 +41,13 @@ class TestSearchOrganization(TestBase):
         # no way for us to check the url…
         assert str(self.org_alpha.domain) in content
 
+    def test_search_ignores_case(self):
+        response = self.search_for('AlPhA', in_organizations=True)
+        assert response.status_code == 200
+        content = response.content.decode('utf-8')
+        # no way for us to check the url…
+        assert str(self.org_alpha.domain) in content
+
     def test_search_for_organization_by_full_domain(self):
         response = self.search_for('beta.com', in_organizations=True)
         assert response.status_code == 200
diff --git a/workshops/views.py b/workshops/views.py
index c4d77196..826096aa 100644
--- a/workshops/views.py
+++ b/workshops/views.py
@@ -184,7 +184,7 @@ def admin_dashboard(request):
 
     uninvoiced_events = Event.objects.active().uninvoiced_events()
 
-    # This annotation may produce wrong number of instructors when 
+    # This annotation may produce wrong number of instructors when
     # `unpublished_events` filters out events that contain a specific tag.
     # The bug was fixed in #1130.
     unpublished_events = Event.objects \\
@@ -1731,22 +1731,22 @@ def search(request):
 
             if form.cleaned_data['in_organizations']:
                 organizations = Organization.objects.filter(
-                    Q(domain__contains=term) |
-                    Q(fullname__contains=term) |
-                    Q(notes__contains=term)) \\
+                    Q(domain__icontains=term) |
+                    Q(fullname__icontains=term) |
+                    Q(notes__icontains=term)) \\
                     .order_by('fullname')
                 results += list(organizations)
 
             if form.cleaned_data['in_events']:
                 events = Event.objects.filter(
-                    Q(slug__contains=term) |
-                    Q(notes__contains=term) |
-                    Q(host__domain__contains=term) |
-                    Q(host__fullname__contains=term) |
-                    Q(url__contains=term) |
-                    Q(contact__contains=term) |
-                    Q(venue__contains=term) |
-                    Q(address__contains=term)
+                    Q(slug__icontains=term) |
+                    Q(notes__icontains=term) |
+                    Q(host__domain__icontains=term) |
+                    Q(host__fullname__icontains=term) |
+                    Q(url__icontains=term) |
+                    Q(contact__icontains=term) |
+                    Q(venue__icontains=term) |
+                    Q(address__icontains=term)
                 ).order_by('-slug')
                 results += list(events)
 
@@ -1756,36 +1756,36 @@ def search(request):
                 if len(tokens) == 2:
                     name1, name2 = tokens
                     complex_q = (
-                        Q(personal__contains=name1) & Q(family__contains=name2)
+                        Q(personal__icontains=name1) & Q(family__icontains=name2)
                     ) | (
-                        Q(personal__contains=name2) & Q(family__contains=name1)
-                    ) | Q(email__contains=term) | Q(github__contains=term)
+                        Q(personal__icontains=name2) & Q(family__icontains=name1)
+                    ) | Q(email__icontains=term) | Q(github__icontains=term)
                     persons = Person.objects.filter(complex_q)
                 else:
                     persons = Person.objects.filter(
-                        Q(personal__contains=term) |
-                        Q(family__contains=term) |
-                        Q(email__contains=term) |
-                        Q(github__contains=term)) \\
+                        Q(personal__icontains=term) |
+                        Q(family__icontains=term) |
+                        Q(email__icontains=term) |
+                        Q(github__icontains=term)) \\
                         .order_by('family')
                 results += list(persons)
 
             if form.cleaned_data['in_airports']:
                 airports = Airport.objects.filter(
-                    Q(iata__contains=term) |
-                    Q(fullname__contains=term)) \\
+                    Q(iata__icontains=term) |
+                    Q(fullname__icontains=term)) \\
                     .order_by('iata')
                 results += list(airports)
 
             if form.cleaned_data['in_training_requests']:
                 training_requests = TrainingRequest.objects.filter(
-                    Q(group_name__contains=term) |
-                    Q(family__contains=term) |
-                    Q(email__contains=term) |
-                    Q(github__contains=term) |
-                    Q(affiliation__contains=term) |
-                    Q(location__contains=term) |
-                    Q(comment__contains=term)
+                    Q(group_name__icontains=term) |
+                    Q(family__icontains=term) |
+                    Q(email__icontains=term) |
+                    Q(github__icontains=term) |
+                    Q(affiliation__icontains=term) |
+                    Q(location__icontains=term) |
+                    Q(comment__icontains=term)
                 )
                 results += list(training_requests)