diff options
-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 |