Generic autocompletion view

This commit is contained in:
Martin Pépin 2020-07-01 22:29:07 +02:00
parent 30783d677b
commit e7517195cd
No known key found for this signature in database
GPG key ID: E7520278B1774448
20 changed files with 272 additions and 309 deletions

View file

@ -1,5 +1,7 @@
from django.contrib.auth import get_user_model
from django.db.models import Q
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from shared import autocomplete
@ -14,12 +16,16 @@ class KfetAccountSearch(autocomplete.ModelSearch):
"last_name",
"profile__account_kfet__trigramme",
]
verbose_name = _("Comptes existants")
def get_queryset_filter(self, *args, **kwargs):
qset_filter = super().get_queryset_filter(*args, **kwargs)
qset_filter &= Q(profile__account_kfet__isnull=False)
return qset_filter
def result_verbose_name(self, user):
return "{} ({})".format(user, user.profile.account_kfet.trigramme)
def result_uuid(self, user):
return user.username
@ -27,6 +33,7 @@ class KfetAccountSearch(autocomplete.ModelSearch):
class COFMemberSearch(autocomplete.ModelSearch):
model = User
search_fields = ["username", "first_name", "last_name"]
verbose_name = _("Membres du COF")
def get_queryset_filter(self, *args, **kwargs):
qset_filter = super().get_queryset_filter(*args, **kwargs)
@ -36,10 +43,14 @@ class COFMemberSearch(autocomplete.ModelSearch):
def result_uuid(self, user):
return user.username
def result_link(self, user):
return reverse("kfet.account.create.fromuser", args=(user.username,))
class OthersSearch(autocomplete.ModelSearch):
model = User
search_fields = ["username", "first_name", "last_name"]
verbose_name = _("Non-membres du COF")
def get_queryset_filter(self, *args, **kwargs):
qset_filter = super().get_queryset_filter(*args, **kwargs)
@ -49,11 +60,25 @@ class OthersSearch(autocomplete.ModelSearch):
def result_uuid(self, user):
return user.username
def result_link(self, user):
return reverse("kfet.account.create.fromuser", args=(user.username,))
class KfetLDAPSearch(autocomplete.LDAPSearch):
def result_link(self, clipper):
return reverse(
"kfet.account.create.fromclipper", args=(clipper.clipper, clipper.fullname)
)
class KfetAutocomplete(autocomplete.Compose):
search_units = [
("kfet", KfetAccountSearch()),
("users_cof", COFMemberSearch()),
("users_notcof", OthersSearch()),
("clippers", autocomplete.LDAPSearch()),
("clippers", KfetLDAPSearch()),
]
class KfetAccountOnlyAutocomplete(autocomplete.Compose):
search_units = [("kfet", KfetAccountSearch())]

View file

@ -159,7 +159,7 @@
background:rgba(255,255,255,0.9);
}
#search_results ul li.user_category {
#search_results ul li.autocomplete-header {
font-weight:bold;
background:#c8102e;
color:#fff;
@ -178,7 +178,7 @@
text-decoration:none;
}
#search_results ul li span.text {
#search_results ul li span.autocomplete-item {
display:block;
padding:5px 20px;
}

View file

@ -1,50 +0,0 @@
{% load kfet_tags %}
<ul>
<li>
<a href="{% url "kfet.account.create.empty" %}">
Créer un compte
</a>
</li>
{% if kfet %}
<li class="user_category"><span class="text">Comptes existants</span></li>
{% for user in kfet %}
<li><span class="text">{{ user.account_kfet.account }} [{{ user|highlight_user:q }}]</span></li>
{% endfor %}
{% endif %}
{% if users_cof %}
<li class="user_category"><span class="text">Membres du COF</span></li>
{% for user in users_cof %}
<li>
<a href="{% url "kfet.account.create.fromuser" user.username %}">
{{ user|highlight_user:q }}
</a>
</li>
{% endfor %}
{% endif %}
{% if users_notcof %}
<li class="user_category"><span class="text">Non-membres du COF</span></li>
{% for user in users_notcof %}
<li>
<a href="{% url "kfet.account.create.fromuser" user.username %}">
{{ user|highlight_user:q }}
</a>
</li>
{% endfor %}
{% endif %}
{% if clippers %}
<li class="user_category"><span class="text">Utilisateurs clipper</span></li>
{% for clipper in clippers %}
<li>
<a href="{% url "kfet.account.create.fromclipper" clipper.clipper clipper.fullname%}">
{{ clipper|highlight_clipper:q }}
</a>
</li>
{% endfor %}
{% endif %}
{% if not q %}
<li class="user_category"><span class="text">Pas de recherche, pas de résultats !</span></li>
{% elif not options %}
<li class="user_category"><span class="text">Aucune correspondance trouvée :-(</span></li>
{% endif %}
</ul>

View file

@ -1,14 +0,0 @@
{% load kfet_tags %}
<ul>
{% if accounts %}
{% for trigramme, user in accounts %}
<li class='choice'>{{ user|highlight_text:q }} (<span class="trigramme">{{ trigramme }}</span>)</li>
{% endfor %}
{% elif not q %}
<li class="user_category"><span class="text">Pas de recherche, pas de résultats !</span></li>
{% else %}
<li class="user_category"><span class="text">Aucune correspondance trouvée :-(</span></li>
{% endif %}
</ul>

View file

@ -1,8 +1,4 @@
import re
from django import template
from django.utils.html import escape
from django.utils.safestring import mark_safe
from ..utils import to_ukf
@ -11,40 +7,14 @@ register = template.Library()
register.filter("ukf", to_ukf)
@register.filter()
def highlight_text(text, q):
q2 = "|".join(re.escape(word) for word in q.split())
pattern = re.compile(r"(?P<filter>%s)" % q2, re.IGNORECASE)
regex = r"<span class='highlight_autocomplete'>\g<filter></span>"
return mark_safe(re.sub(pattern, regex, escape(text)))
@register.filter(is_safe=True)
def highlight_user(user, q):
if user.first_name and user.last_name:
text = "%s %s (%s)" % (user.first_name, user.last_name, user.username)
else:
text = user.username
return highlight_text(text, q)
@register.filter(is_safe=True)
def highlight_clipper(clipper, q):
if clipper.fullname:
text = "%s (%s)" % (clipper.fullname, clipper.clipper)
else:
text = clipper.clipper
return highlight_text(text, q)
@register.filter()
def widget_type(field):
return field.field.widget.__class__.__name__
@register.filter()
def slice(l, start, end=None):
def slice(t, start, end=None):
if end is None:
end = start
start = 0
return l[start:end]
return t[start:end]

View file

@ -181,9 +181,15 @@ class AccountCreateAutocompleteViewTests(ViewTestCaseMixin, TestCase):
def test_ok(self):
r = self.client.get(self.url, {"q": "first"})
self.assertEqual(r.status_code, 200)
self.assertEqual(len(r.context["users_notcof"]), 0)
self.assertEqual(len(r.context["users_cof"]), 0)
self.assertSetEqual(set(r.context["kfet"]), set([self.users["user"]]))
self.assertEqual(len(r.context["results"]), 1)
(res,) = r.context["results"]
self.assertEqual(res.name, "kfet")
u = self.users["user"]
self.assertSetEqual(
{e.verbose_name for e in res.entries},
{"{} ({})".format(u, u.profile.account_kfet.trigramme)},
)
class AccountSearchViewTests(ViewTestCaseMixin, TestCase):
@ -196,7 +202,12 @@ class AccountSearchViewTests(ViewTestCaseMixin, TestCase):
def test_ok(self):
r = self.client.get(self.url, {"q": "first"})
self.assertEqual(r.status_code, 200)
self.assertSetEqual(set(r.context["accounts"]), set([("000", "first last")]))
u = self.users["user"]
self.assertSetEqual(
{e.verbose_name for e in r.context["results"][0].entries},
{"{} ({})".format(u, u.profile.account_kfet.trigramme)},
)
class AccountReadViewTests(ViewTestCaseMixin, TestCase):

View file

@ -27,7 +27,7 @@ from django.views.generic.edit import CreateView, DeleteView, UpdateView
from gestioncof.models import CofProfile
from kfet import KFET_DELETED_TRIGRAMME, consumers
from kfet.auth.decorators import kfet_password_auth
from kfet.autocomplete import KfetAccountSearch, KfetAutocomplete
from kfet.autocomplete import KfetAccountOnlyAutocomplete, KfetAutocomplete
from kfet.config import kfet_config
from kfet.decorators import teamkfet_required
from kfet.forms import (
@ -79,6 +79,7 @@ from kfet.models import (
TransferGroup,
)
from kfet.statistic import DayScale, MonthScale, ScaleMixin, WeekScale, scale_url_params
from shared.views import AutocompleteView
from .auth import KFET_GENERIC_TRIGRAMME
from .auth.views import ( # noqa
@ -2594,34 +2595,11 @@ class ArticleStatSales(ScaleMixin, JSONDetailView):
# ---
class AccountCreateAutocompleteView(PermissionRequiredMixin, TemplateView):
template_name = "kfet/account_create_autocomplete.html"
class AccountCreateAutocompleteView(PermissionRequiredMixin, AutocompleteView):
permission_required = "kfet.is_team"
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
if "q" not in self.request.GET:
raise Http404
q = self.request.GET["q"]
ctx["q"] = q
results = KfetAutocomplete().search(q.split())
ctx["options"] = sum((len(res) for res in results.values()))
ctx.update(results)
return ctx
search_composer = KfetAutocomplete()
class AccountSearchAutocompleteView(PermissionRequiredMixin, TemplateView):
template_name = "kfet/account_search_autocomplete.html"
class AccountSearchAutocompleteView(PermissionRequiredMixin, AutocompleteView):
permission_required = "kfet.is_team"
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
if "q" not in self.request.GET:
raise Http404
q = self.request.GET["q"]
ctx["q"] = q
ctx["accounts"] = [
(user.profile.account_kfet.trigramme, user.get_full_name())
for user in KfetAccountSearch().search(q.split())
]
return ctx
search_composer = KfetAccountOnlyAutocomplete()