Projet

Général

Profil

0001-custom_user-specialize-free_text_search-for-common-s.patch

Benjamin Dauvergne, 11 janvier 2021 10:07

Télécharger (4,12 ko)

Voir les différences:

Subject: [PATCH] custom_user: specialize free_text_search for common search
 terms (#49957)

 src/authentic2/custom_user/managers.py | 59 +++++++++++++++++++++++---
 1 file changed, 54 insertions(+), 5 deletions(-)
src/authentic2/custom_user/managers.py
17 17
import datetime
18 18
import logging
19 19
import unicodedata
20
import uuid
20 21

  
21 22
from django.contrib.contenttypes.models import ContentType
22 23
from django.contrib.postgres.search import TrigramDistance
24
from django.core.exceptions import ValidationError
23 25
from django.db import models, transaction, connection
24 26
from django.db.models import F, Value, FloatField, Subquery, OuterRef
25 27
from django.db.models.functions import Lower, Coalesce
26
from django.utils import six
27
from django.utils import timezone
28
from django.utils import timezone, six
28 29
from django.contrib.auth.models import BaseUserManager
29 30

  
30 31
from authentic2 import app_settings
31 32
from authentic2.models import Attribute, AttributeValue, UserExternalId
32 33
from authentic2.utils.lookups import Unaccent, ImmutableConcat
34
from authentic2.utils.date import parse_date
35
from authentic2.attribute_kinds import clean_number
33 36

  
34 37

  
35 38
class UserQuerySet(models.QuerySet):
36 39

  
37 40
    def free_text_search(self, search):
41
        search = search.strip()
42

  
43
        if '@' in search:
44
            return self.filter(email__icontains=search).order('email')
45

  
46
        try:
47
            guid = uuid.UUID(search)
48
        except ValueError:
49
            pass
50
        else:
51
            return self.filter(uuid=guid.hex)
52

  
53
        try:
54
            phone_number = clean_number(search)
55
        except ValidationError:
56
            pass
57
        else:
58
            attribute_values = AttributeValue.objects.filter(
59
                content__contains=phone_number, attribute__kind='phone_number')
60
            qs = self.filter(attribute_values__in=attribute_values).order_by('last_name', 'first_name')
61
            if qs.exists():
62
                return qs
63

  
64
        try:
65
            date = parse_date(search)
66
        except ValueError:
67
            pass
68
        else:
69
            attribute_values = AttributeValue.objects.filter(
70
                content__contains=date.isoformat(), attribute__kind='birthdate')
71
            qs = self.filter(attribute_values__in=attribute_values).order_by('last_name', 'first_name')
72
            if qs.exists():
73
                return qs
74

  
75
        qs = self.find_duplicates(fullname=search, limit=None)
76
        if qs.exists():
77
            return qs
78

  
79
        qs = self.filter(username__istartswith=search)
80
        if qs.exists():
81
            return qs
82
        return self.free_text_search_complex(search)
83

  
84
    def free_text_search_complex(self, search):
38 85
        terms = search.split()
39 86

  
40 87
        if not terms:
......
44 91
        queries = []
45 92
        for term in terms:
46 93
            q = None
47

  
48 94
            specific_queries = []
49 95
            for a in searchable_attributes:
50 96
                kind = a.get_kind()
......
78 124
            self = self.distinct()
79 125
        return self
80 126

  
81
    def find_duplicates(self, first_name=None, last_name=None, fullname=None, birthdate=None):
127

  
128

  
129
    def find_duplicates(self, first_name=None, last_name=None, fullname=None, birthdate=None, limit=5):
82 130
        with connection.cursor() as cursor:
83 131
            cursor.execute(
84 132
                "SET pg_trgm.similarity_threshold = %f" % app_settings.A2_DUPLICATES_THRESHOLD
......
96 144
        qs = qs.filter(name__trigram_similar=name)
97 145
        qs = qs.annotate(dist=TrigramDistance('name', name))
98 146
        qs = qs.order_by('dist')
99
        qs = qs[:5]
147
        if limit is not None:
148
            qs = qs[:limit]
100 149

  
101 150
        # alter distance according to additionnal parameters
102 151
        if birthdate:
103
-