From 3bb6609c7a172473cc1b3dd0dea0fe14905cc38f Mon Sep 17 00:00:00 2001
From: pka065 <pka065@it6100016.klientdrift.uib.no>
Date: Mon, 10 Oct 2022 16:34:14 +0200
Subject: [PATCH] Change search principle, make an external search method + add
 birth date to Person GUI

---
 greg/api/urls.py              |  6 ++--
 greg/api/views/person.py      | 32 ++++++++++++++++++
 gregui/api/views/person.py    | 62 ++---------------------------------
 search_tools/person_search.py | 60 +++++++++++++++++++++++++++++++++
 4 files changed, 97 insertions(+), 63 deletions(-)
 create mode 100644 search_tools/person_search.py

diff --git a/greg/api/urls.py b/greg/api/urls.py
index 8d8ad36a..9e028ba2 100644
--- a/greg/api/urls.py
+++ b/greg/api/urls.py
@@ -17,7 +17,7 @@ from greg.api.views.sponsor import (
     SponsorGuestsViewSet,
     SponsorOrgunitLinkView,
 )
-from gregui.api.views.person import PersonSearchViewSet
+from greg.api.views.person import PersonSearchSet
 
 router = DefaultRouter(trailing_slash=False)
 router.register(r"persons", PersonViewSet, basename="person")
@@ -76,8 +76,8 @@ urlpatterns += [
         name="sponsor_orgunit-detail",
     ),
     re_path(
-        "person_search/",
-        PersonSearchViewSet.as_view({"get": "list"}),
+        "person-search/",
+        PersonSearchSet.as_view({"get": "list"}),
         name="person-search",
     ),
 ]
diff --git a/greg/api/views/person.py b/greg/api/views/person.py
index 51b87085..ddd4bb47 100644
--- a/greg/api/views/person.py
+++ b/greg/api/views/person.py
@@ -19,6 +19,7 @@ from greg.api.serializers.person import (
 )
 from greg.api.serializers.role import RoleSerializer, RoleWriteSerializer
 from greg.models import Person, Role, Identity, Consent, ConsentChoice, ConsentType
+from search_tools.person_search import person_by_string_query
 
 
 class PersonViewSet(viewsets.ModelViewSet):
@@ -58,6 +59,37 @@ class PersonViewSet(viewsets.ModelViewSet):
         return super().list(request, *args, **kwargs)
 
 
+class PersonSearchSet(viewsets.ModelViewSet):
+    """Search for persons using name, email, phone number and birth date"""
+
+    permission_classes = (permissions.IsAdminUser,)
+
+    def list(self, request, *args, **kwargs):
+        if "q" not in self.request.query_params:
+            return Response(
+                status=status.HTTP_400_BAD_REQUEST,
+                data={
+                    "code": "no_search_term",
+                    "message": "No search query parameter given",
+                },
+            )
+
+        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",
+                },
+            )
+
+        hits = self.get_hits()
+        return Response(hits)
+
+    def get_hits(self):
+        return person_by_string_query(self.request)
+
+
 class RoleViewSet(viewsets.ModelViewSet):
     """Person role API"""
 
diff --git a/gregui/api/views/person.py b/gregui/api/views/person.py
index db651e1c..4cfbafbf 100644
--- a/gregui/api/views/person.py
+++ b/gregui/api/views/person.py
@@ -1,4 +1,3 @@
-from django.db.models import Q
 from rest_framework import mixins
 from rest_framework import status
 from rest_framework.authentication import SessionAuthentication, BasicAuthentication
@@ -16,6 +15,7 @@ from gregui.api.serializers.guest import (
 )
 from gregui.api.serializers.identity import IdentityDuplicateError
 from gregui.models import GregUserProfile
+from search_tools.person_search import person_by_string_query
 
 
 class PersonViewSet(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, GenericViewSet):
@@ -106,65 +106,7 @@ class PersonSearchViewSet(GenericViewSet):
         return Response(hits)
 
     def get_hits(self):
-        search = self.request.query_params["q"]
-        include_birth_date = False
-        if not "gregui" in self.request.version:
-            include_birth_date = True
-
-        split_search = search.split()
-        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 = []
-        # 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:
-            output_data = {
-                "pid": person.id,
-                "first": person.first_name,
-                "last": person.last_name,
-            }
-            if include_birth_date:
-                output_data["date_of_birth"] = person.date_of_birth
-            hits.append(output_data)
-            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 - 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,
-                }
-            )
-
-        return hits
+        return person_by_string_query(self.request)
 
 
 class GuestInfoViewSet(mixins.ListModelMixin, GenericViewSet):
diff --git a/search_tools/person_search.py b/search_tools/person_search.py
new file mode 100644
index 00000000..1fc29edc
--- /dev/null
+++ b/search_tools/person_search.py
@@ -0,0 +1,60 @@
+from django.db.models import Q
+from greg.models import Identity, Person
+
+def person_by_string_query(request):
+    search = request.query_params["q"]
+
+    split_search = search.split()
+    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 = []
+    # 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,
+                "date_of_birth": person.date_of_birth,
+            }
+        )
+        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 - 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,
+            }
+        )
+
+    return hits
-- 
GitLab