From 279b795152e290bc7d052b4b63ca4cae59ad4866 Mon Sep 17 00:00:00 2001
From: Tore Brede <Tore.Brede@uib.no>
Date: Thu, 6 Jan 2022 12:52:25 +0100
Subject: [PATCH] GREG-161: Updating test and formatting

---
 greg/api/serializers/person.py        |  2 +-
 gregui/api/urls.py                    |  7 +++-
 gregui/api/views/person.py            | 55 ++++++++++++++++++---------
 gregui/tests/api/views/test_search.py | 31 ++++++++++++---
 gregui/tests/conftest.py              | 10 ++---
 5 files changed, 74 insertions(+), 31 deletions(-)

diff --git a/greg/api/serializers/person.py b/greg/api/serializers/person.py
index 3d299897..c7d1e085 100644
--- a/greg/api/serializers/person.py
+++ b/greg/api/serializers/person.py
@@ -4,7 +4,7 @@ from rest_framework.fields import BooleanField, CharField, SerializerMethodField
 from greg.api.serializers.consent import ConsentSerializerBrief
 from greg.api.serializers.identity import IdentitySerializer, SpecialIdentitySerializer
 from greg.api.serializers.role import RoleSerializer, SpecialRoleSerializer
-from greg.models import Person, Identity
+from greg.models import Person
 
 
 class PersonSerializer(serializers.ModelSerializer):
diff --git a/gregui/api/urls.py b/gregui/api/urls.py
index 008cc828..5fed05cb 100644
--- a/gregui/api/urls.py
+++ b/gregui/api/urls.py
@@ -36,7 +36,10 @@ urlpatterns += [
         name="invite-resend",
     ),
     path("invite/", InvitationView.as_view(), name="invitation"),
-    path("person/search/", PersonSearchViewSet.as_view({"get": "list"}), name="person-search",
-         ),
+    path(
+        "person/search/",
+        PersonSearchViewSet.as_view({"get": "list"}),
+        name="person-search",
+    ),
     path("userinfo/", UserInfoView.as_view(), name="userinfo"),
 ]
diff --git a/gregui/api/views/person.py b/gregui/api/views/person.py
index cc281a2b..622d9ff7 100644
--- a/gregui/api/views/person.py
+++ b/gregui/api/views/person.py
@@ -40,7 +40,7 @@ class PersonViewSet(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, GenericV
 
 
 class PersonSearchViewSet(GenericViewSet):
-    """Search for persons using email or phone number"""
+    """Search for persons using name, email, phone number and birth date"""
 
     authentication_classes = [SessionAuthentication, BasicAuthentication]
     permission_classes = [IsAuthenticated, IsSponsor]
@@ -56,12 +56,13 @@ class PersonSearchViewSet(GenericViewSet):
             )
 
         if len(self.request.query_params["q"]) > 50:
-            return Response(status=status.HTTP_400_BAD_REQUEST,
-                            data={
-                                "code": "search_term_too_large",
-                                "message": "Search term is too large",
-                            },
-                            )
+            return Response(
+                status=status.HTTP_400_BAD_REQUEST,
+                data={
+                    "code": "search_term_too_large",
+                    "message": "Search term is too large",
+                },
+            )
 
         hits = self.get_hits()
         return Response(hits)
@@ -70,32 +71,52 @@ class PersonSearchViewSet(GenericViewSet):
         search = self.request.query_params["q"]
 
         split_search = search.split()
-        words_joined = '|'.join(map(str, split_search))
-        search_regex = r'^(%s)' % words_joined
+        words_joined = "|".join(map(str, split_search))
+        # Create a regex with the terms in the search or-ed together. This will trigger a match
+        # if one of the fields that are being searched contains one of the terms
+        search_regex = r"^(%s)" % words_joined
 
         hits = []
-        persons = Person.objects.filter(Q(first_name__iregex=search_regex) |
-                                        Q(last_name__iregex=search_regex) |
-                                        Q(date_of_birth__iregex=search_regex))[:10]
+        # First look for hits on name and birth date
+        persons = Person.objects.filter(
+            Q(first_name__iregex=search_regex)
+            | Q(last_name__iregex=search_regex)
+            | Q(date_of_birth__iregex=search_regex)
+        )[:10]
 
         included_persons = []
         for person in persons:
-            hits.append({"pid": person.id, "first": person.first_name, "last": person.last_name})
+            hits.append(
+                {"pid": person.id, "first": person.first_name, "last": person.last_name}
+            )
             included_persons.append(person.id)
 
+        if len(hits) == 10:
+            # Max number of hits, no need to search more
+            return hits
+
+        # Look for hits in e-mail and mobile phone
         identities = Identity.objects.filter(
             value__iregex=search_regex,
             type__in=[
                 Identity.IdentityType.PRIVATE_EMAIL,
                 Identity.IdentityType.PRIVATE_MOBILE_NUMBER,
             ],
-        )[:10]
+        )[: (10 - len(hits))]
 
         for identity in identities:
             if identity.person_id in included_persons:
                 continue
 
-            hits.append({"pid": identity.person_id, "first": identity.person.first_name, "last": identity.person.last_name, "value": identity.value, "type": identity.type})
+            hits.append(
+                {
+                    "pid": identity.person_id,
+                    "first": identity.person.first_name,
+                    "last": identity.person.last_name,
+                    "value": identity.value,
+                    "type": identity.type,
+                }
+            )
 
         return hits
 
@@ -126,6 +147,6 @@ class GuestInfoViewSet(mixins.ListModelMixin, GenericViewSet):
         units = user.sponsor.get_allowed_units()
         return (
             Person.objects.filter(roles__orgunit__in=list(units))
-                .distinct()
-                .order_by("id")
+            .distinct()
+            .order_by("id")
         )
diff --git a/gregui/tests/api/views/test_search.py b/gregui/tests/api/views/test_search.py
index 29157589..03422aec 100644
--- a/gregui/tests/api/views/test_search.py
+++ b/gregui/tests/api/views/test_search.py
@@ -4,8 +4,8 @@ from rest_framework.reverse import reverse
 
 
 @pytest.mark.django_db
-def test_name_search(client, log_in, user_sponsor, create_person):
-    person = create_person(
+def test_no_search_parameter_fails(client, log_in, user_sponsor, create_person):
+    create_person(
         first_name="foo",
         last_name="bar",
         email="foo@bar.com",
@@ -26,7 +26,7 @@ def test_date_of_birth_search(client, log_in, user_sponsor, create_person):
         first_name="foo",
         last_name="bar",
         email="foo@bar.com",
-        date_of_birth="2005-06-20"
+        date_of_birth="2005-06-20",
     )
 
     url = reverse("gregui-v1:person-search") + "?q=2005-06-20"
@@ -47,18 +47,37 @@ def test_multiple_words_search(client, log_in, user_sponsor, create_person):
     person = create_person(
         first_name="foo",
         last_name="bar",
+        email="example@company.com",
+    )
+    person2 = create_person(
+        first_name="test",
+        last_name="test2",
         email="foo@bar.com",
-        date_of_birth="2005-06-20"
+        date_of_birth="2006-06-20",
+    )
+    person3 = create_person(
+        first_name="Bob",
+        last_name="Smith",
+        email="bob@smith.com",
+        date_of_birth="2005-06-20",
+    )
+    person4 = create_person(
+        first_name="Frank",
+        last_name="Paulsen",
+        email="frank@paulsen.com",
     )
 
-    url = reverse("gregui-v1:person-search") + "?q=foo%20bar"
+    url = reverse("gregui-v1:person-search") + "?q=foo%20bar%202005-06-20"
 
     log_in(user_sponsor)
     response = client.get(url)
 
-    assert len(response.data) == 1
+    assert len(response.data) == 3
 
     person_ids = list(map(lambda x: x["pid"], response.data))
 
     assert person.id in person_ids
+    assert person2.id in person_ids
+    assert person3.id in person_ids
+    assert person4.id not in person_ids
     assert response.status_code == status.HTTP_200_OK
diff --git a/gregui/tests/conftest.py b/gregui/tests/conftest.py
index 694e6769..565bebee 100644
--- a/gregui/tests/conftest.py
+++ b/gregui/tests/conftest.py
@@ -326,7 +326,9 @@ def invitation_link_expired(
 
 
 @pytest.fixture
-def create_person() -> Callable[[str, str, str, Optional[str], Optional[str], Optional[datetime.date]], Person]:
+def create_person() -> Callable[
+    [str, str, str, Optional[str], Optional[str], Optional[datetime.date]], Person
+]:
     # TODO fix the typing...
     def create_person(
         first_name: str,
@@ -334,12 +336,10 @@ def create_person() -> Callable[[str, str, str, Optional[str], Optional[str], Op
         email: str = None,
         nin: str = None,
         feide_id: str = None,
-        date_of_birth: datetime.date = None
+        date_of_birth: datetime.date = None,
     ) -> Person:
         person = Person.objects.create(
-            first_name=first_name,
-            last_name=last_name,
-            date_of_birth=date_of_birth
+            first_name=first_name, last_name=last_name, date_of_birth=date_of_birth
         )
 
         if nin:
-- 
GitLab