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