diff options
| author | Étienne Loks <etienne.loks@iggdrasil.net> | 2019-05-16 16:39:55 +0200 | 
|---|---|---|
| committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2019-05-16 16:39:55 +0200 | 
| commit | c573609f76f5d0081cde0727f52a69a62d388e10 (patch) | |
| tree | ec391d3cae5842a74159eb452cd52b404e1cf19e | |
| parent | 0f48778cee5d7f47fac9163e6ce032a3f8b3821a (diff) | |
| download | Ishtar-c573609f76f5d0081cde0727f52a69a62d388e10.tar.bz2 Ishtar-c573609f76f5d0081cde0727f52a69a62d388e10.zip | |
Autocomplete: filter by more relevant search term
| -rw-r--r-- | archaeological_finds/tests.py | 39 | ||||
| -rw-r--r-- | archaeological_finds/views.py | 29 | ||||
| -rw-r--r-- | ishtar_common/views_item.py | 69 | 
3 files changed, 106 insertions, 31 deletions
| diff --git a/archaeological_finds/tests.py b/archaeological_finds/tests.py index 3c9995632..d9b79be89 100644 --- a/archaeological_finds/tests.py +++ b/archaeological_finds/tests.py @@ -937,6 +937,45 @@ class FindSearchTest(FindInit, TestCase):          self._test_search(c, result) +class FindAutocompleteTest(FindInit, TestCase): +    fixtures = WAREHOUSE_FIXTURES +    model = models.Find + +    def setUp(self): +        self.create_finds(force=True) +        self.create_finds(force=True) +        self.create_finds(force=True) +        self.create_finds(force=True) +        self.username = 'myuser' +        self.password = 'mypassword' +        User.objects.create_superuser(self.username, 'myemail@test.com', +                                      self.password) +        self.client = Client() + +    def test_autocomplete(self): +        find = self.finds[0] +        find.label = "test 012" +        find.save() +        find2 = self.finds[1] +        find2.label = "test 12" +        find2.save() +        find3 = self.finds[2] +        find3.label = "test 1" +        find3.save() +        find4 = self.finds[3] +        find4.label = "test 0120" +        find4.save() +        c = Client() +        c.login(username=self.username, password=self.password) +        response = c.get(reverse('autocomplete-find') + "?term=12") +        self.assertEqual(response.status_code, 200) +        res = json.loads(response.content) +        self.assertEqual(len(res), 3) +        self.assertEqual(res[0]['id'], find2.pk)  # " 12" - startswith +        self.assertEqual(res[1]['id'], find.pk)  # 12 - endswith +        self.assertEqual(res[2]['id'], find4.pk)  # 12 - contains + +  class FindPermissionTest(FindInit, TestCase):      fixtures = FIND_FIXTURES      model = models.Find diff --git a/archaeological_finds/views.py b/archaeological_finds/views.py index 72d02305c..9e1a22a2a 100644 --- a/archaeological_finds/views.py +++ b/archaeological_finds/views.py @@ -17,6 +17,7 @@  # See the file COPYING for details. +from collections import OrderedDict  import json  from django.core.exceptions import PermissionDenied @@ -42,7 +43,7 @@ from archaeological_finds import forms  from ishtar_common.views import get_autocomplete_generic, IshtarMixin, \      LoginRequiredMixin, QAItemEditForm, QAItemForm  from ishtar_common.views_item import display_item, get_item, show_item, \ -    revert_item, get_autocomplete_item, get_autocomplete_query +    revert_item, get_autocomplete_item, get_autocomplete_queries  from archaeological_operations.wizards import AdministrativeActDeletionWizard  from archaeological_finds import wizards @@ -152,17 +153,23 @@ def autocomplete_findbasket(request, current_right=None):      if not request.GET.get('term'):          return HttpResponse(content_type='text/plain') -    query = get_autocomplete_query(request, ['label'])      limit = 20 -    query = query & models.FindBasket.get_write_query_owns( -        request.user.ishtaruser) -    items = models.FindBasket.objects.filter(query).distinct().order_by( -        'label')[:limit] -    data = json.dumps( -        [{'id': item.pk, -          'value': u"{} - {}".format(item.label, item.user)[:60]} -         for item in items] -    ) +    result = OrderedDict() +    for query in get_autocomplete_queries(request, ['label']): +        query = query & models.FindBasket.get_write_query_owns( +            request.user.ishtaruser) +        objects = models.FindBasket.objects.filter(query).distinct().order_by( +            'label')[:limit] +        for obj in objects: +            if obj.id not in list(result.keys()): +                result[obj.id] = u"{} - {}".format(obj.label, obj.user)[:60] +                limit -= 1 +                if not limit: +                    break +        if not limit: +            break +    data = json.dumps([{'id': obj[0], 'value': obj[1]} +                       for obj in list(result.items())])      return HttpResponse(data, content_type='text/plain') diff --git a/ishtar_common/views_item.py b/ishtar_common/views_item.py index 57d778459..96190f083 100644 --- a/ishtar_common/views_item.py +++ b/ishtar_common/views_item.py @@ -1,6 +1,7 @@  #!/usr/bin/env python  # -*- coding: utf-8 -*- +from collections import OrderedDict  from copy import copy, deepcopy  import csv  import datetime @@ -67,33 +68,61 @@ CURRENT_ITEM_KEYS = (  CURRENT_ITEM_KEYS_DICT = dict(CURRENT_ITEM_KEYS) -def get_autocomplete_query(request, label_attributes, extra=None): -    q = request.GET.get('term') or "" +def get_autocomplete_queries(request, label_attributes, extra=None):      if not label_attributes: -        return Q(pk__isnull=True) -    query = Q() -    if extra: -        query = Q(**extra) -    for q in q.split(' '): -        if not q: -            continue -        sub_q = Q(**{label_attributes[0] + "__icontains": q}) -        for other_label in label_attributes[1:]: -            sub_q = sub_q | Q(**{other_label + "__icontains": q}) -        query = query & sub_q -    return query +        return [Q(pk__isnull=True)] +    base_q = request.GET.get('term') or "" +    queries = [] +    splited_q = base_q.split(' ') +    for value_prefix, query_suffix, query_endswith in ( +            ('', '__startswith', True),  # starts with +            (' ', '__icontains', True),  # contain a word which starts with +            ('', '__endswith', False),  # ends with +            ('', '__icontains', False)):  # contains +        alt_queries = [None] +        if len(splited_q) == 1 and query_endswith: +            alt_queries = ["__endswith", None] +        for alt_query in alt_queries: +            query = Q() +            if extra: +                query = Q(**extra) +            for q in splited_q: +                if not q: +                    continue +                sub_q = Q( +                    **{label_attributes[0] + query_suffix: value_prefix + q}) +                if alt_query: +                    sub_q &= Q( +                        **{label_attributes[0] + alt_query: q} +                    ) +                for other_label in label_attributes[1:]: +                    sub_q = sub_q | Q( +                        **{other_label + query_suffix: value_prefix + q}) +                query = query & sub_q +            queries.append(query) +    return queries  def get_autocomplete_item(model, extra=None):      if not extra:          extra = {} -    def func(request, current_right=None): -        query = get_autocomplete_query(request, ['cached_label'], extra=extra) -        limit = 20 -        objects = model.objects.filter(query)[:limit] -        data = json.dumps([{'id': obj.pk, 'value': obj.cached_label} -                           for obj in objects]) +    def func(request, current_right=None, limit=20): +        result = OrderedDict() +        for query in get_autocomplete_queries(request, ['cached_label'], +                                              extra=extra): +            objects = model.objects.filter(query).values( +                'cached_label', 'id')[:limit] +            for obj in objects: +                if obj["id"] not in list(result.keys()): +                    result[obj["id"]] = obj["cached_label"] +                    limit -= 1 +                    if not limit: +                        break +            if not limit: +                break +        data = json.dumps([{'id': obj[0], 'value': obj[1]} +                           for obj in list(result.items())])          return HttpResponse(data, content_type='text/plain')      return func | 
