Amélioration de la recherche

This commit is contained in:
Jean-Marie Favreau 2023-11-11 12:03:47 +01:00
parent 5dd1125a68
commit 7097aa17d3
5 changed files with 123 additions and 55 deletions

View File

@ -298,9 +298,9 @@ article#filters {
}
}
.helptext {
.helptext, .location-search {
font-size: 80%;
opacity: 0.7;
margin-top: -0.7em;
}
.django-ckeditor-widget {
@ -317,3 +317,15 @@ article#filters {
.slide-buttons {
float: right;
}
.highlight {
color: var(--primary);
font-weight: bold;
font-style: italic;
}
.search .description {
margin-left: 1em;
font-size: 90%;
margin-top: -0.5em;
}

View File

@ -1,57 +1,64 @@
{% extends "agenda_culturel/page.html" %}
{% block title %}Rechercher un évnement{% endblock %}
{% block title %}Rechercher un événement{% endblock %}
{% load utils_extra %}
{% block content %}
<article>
<article class="search">
<header>
<h1>Rechercher un événement</h1>
</header>
<form method="get" class="form django-form" action="#results">
{{ filter.form }}
<button type="submit">Rechercher</button>
<form method="get" class="form django-form">
{% for field in filter.form %}
<div>
{{ field.errors }}
{{ field.label_tag }} {{ field }}
</div>
{% if forloop.first %}
<details>
<summary>Rechercher avancée</summary>
{% if full %}
<a href="{% url 'event_search' %}">Recherche simplifiée {% picto_from_name "chevron-right" %}</a>
{% else %}
<a href="{% url 'event_search_full' %}">Recherche avancée {% picto_from_name "chevron-right" %}</a>
{% endif %}
{% if forloop.last %}
</details>
{% endif %}
{% endfor %}
<button type="submit">Rechercher</button>
</form>
<ul>
{% for obj in paginator_filter %}
<li><a href="{{ obj.get_absolute_url }}">{{ obj }}</a></li>
{% endfor %}
</ul>
<footer>
<span>
{% if paginator_filter.has_previous %}
<a href="?page=1" role="button">&laquo; premier</a>
<a href="?page={{ paginator_filter.previous_page_number }}" role="button">précédent</a>
{% endif %}
{% if has_results %}
<div id="results">
<p>{{ paginator_filter.paginator.count }} événement{{paginator_filter.object_list.count | pluralize }} correspond{{paginator_filter.object_list.count | pluralize:"ent" }} à la recherche.</p>
<ul>
{% for obj in paginator_filter %}
<li><p>{% include "agenda_culturel/date-times-inc.html" with event=obj %}
&nbsp;: <a href="{{ obj.get_absolute_url }}">
{% if obj.title_hl %}{{ obj.title_hl | safe }}{% else %}{{ obj.title }}{% endif %}</a></p>
<p class="location-search">{% picto_from_name "map-pin" %}
{% if obj.location_hl %}{{ obj.location_hl | safe }}{% else %}{{ obj.location }}{% endif %}</p>
<div class="description">
{% if obj.description_hl %}{{ obj.description_hl | safe }}{% else %}{{ obj.description }}{% endif %}
</div>
</li>
{% endfor %}
</ul>
{% if paginator_filter.paginator.num_pages != 1 %}
<footer>
<span>
{% if paginator_filter.has_previous %}
<a href="?page=1" role="button">&laquo; premier</a>
<a href="?page={{ paginator_filter.previous_page_number }}" role="button">précédent</a>
{% endif %}
<span>
Page {{ paginator_filter.number }} sur {{ paginator_filter.paginator.num_pages }}
</span>
<span>
Page {{ paginator_filter.number }} sur {{ paginator_filter.paginator.num_pages }}
</span>
{% if paginator_filter.has_next %}
<a href="?page={{ paginator_filter.next_page_number }}" role="button">suivant</a>
<a href="?page={{ paginator_filter.paginator.num_pages }}" role="button">dernier &raquo;</a>
{% if paginator_filter.has_next %}
<a href="?page={{ paginator_filter.next_page_number }}" role="button">suivant</a>
<a href="?page={{ paginator_filter.paginator.num_pages }}" role="button">dernier &raquo;</a>
{% endif %}
</span>
</footer>
{% endif %}
</span>
</footer>
</div>
{% endif %}
</article>
{% endblock %}

View File

@ -59,6 +59,10 @@ def url_day(d):
@register.simple_tag
def picto_from_name(name, datatooltip=""):
return mark_safe('<span data-tooltip="' + datatooltip + '"><svg width="1em" height="1em" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">' + \
result = '<svg width="1em" height="1em" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">' + \
'<use href="' + static("images/feather-sprite.svg") + '#' + name + '" />' + \
'</svg></span>')
'</svg>'
if datatooltip != "":
result = '<span data-tooltip="' + datatooltip + '">' + result + '</span>'
return mark_safe(result)

View File

@ -28,7 +28,8 @@ urlpatterns = [
path("test_app/", include("test_app.urls")),
path("static-content/create", StaticContentCreateView.as_view(), name="create_static_content"),
path("static-content/<int:pk>/edit", StaticContentUpdateView.as_view(), name="edit_static_content"),
path('rechercher/', event_search, name='event_search')
path('rechercher/', event_search, name='event_search'),
path('rechercher/complet/', event_search_full, name='event_search_full')
]
if settings.DEBUG:

View File

@ -4,6 +4,8 @@ from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import QueryDict
from django import forms
from django.contrib.postgres.search import SearchQuery, SearchHeadline
from .forms import EventSubmissionModelForm
from .celery import create_event_from_submission
@ -403,16 +405,34 @@ def event_list(request):
return render(request, 'agenda_culturel/list.html', {'filter': filter, 'paginator_filter': response})
class SimpleSearchEventFilter(django_filters.FilterSet):
q = django_filters.CharFilter(method='custom_filter', label=_("Search"))
def custom_filter(self, queryset, name, value):
search_query = SearchQuery(value, config='french')
qs = queryset.filter(
Q(title__contains=value) | Q(location__contains=value) | Q(description__contains=value))
for f in ["title", "location", "description"]:
params = { f + "_hl": SearchHeadline(f,
search_query,
start_sel="<span class=\"highlight\">",
stop_sel="</span>",
config='french')}
qs = qs.annotate(**params)
return qs
class Meta:
model = Event
fields = ['q']
class SearchEventFilter(django_filters.FilterSet):
tags = django_filters.CharFilter(lookup_expr='icontains')
title = django_filters.CharFilter(lookup_expr='contains')
location = django_filters.CharFilter(lookup_expr='contains')
description = django_filters.CharFilter(lookup_expr='contains')
title = django_filters.CharFilter(method="hl_filter_contains")
location = django_filters.CharFilter(method="hl_filter_contains")
description = django_filters.CharFilter(method="hl_filter_contains")
start_day = django_filters.DateFromToRangeFilter(widget=django_filters.widgets.RangeWidget(attrs={'type': 'date'}))
q = django_filters.CharFilter(method='custom_filter', label=_("Search"))
o = django_filters.OrderingFilter(
# tuple-mapping retains order
fields=(
@ -422,18 +442,36 @@ class SearchEventFilter(django_filters.FilterSet):
),
)
def custom_filter(self, queryset, name, value):
return queryset.filter(
Q(title__contains=value) | Q(location__contains=value) | Q(description__contains=value))
def hl_filter_contains(self, queryset, name, value):
# first check if it contains
filter_contains = { name + "__contains": value }
queryset = queryset.filter(**filter_contains)
# then hightlight the result
search_query = SearchQuery(value, config='french')
params = { name + "_hl": SearchHeadline(name,
search_query,
start_sel="<span class=\"highlight\">",
stop_sel="</span>",
config='french')}
return queryset.annotate(**params)
class Meta:
model = Event
fields = ['q', 'title', 'location', 'description', 'category', 'tags', 'start_day']
fields = ['title', 'location', 'description', 'category', 'tags', 'start_day']
def event_search(request, full=False):
if full:
filter = SearchEventFilter(request.GET, queryset=get_event_qs(request).order_by("-start_day"))
else:
filter = SimpleSearchEventFilter(request.GET, queryset=get_event_qs(request).order_by("-start_day"))
def event_search(request):
filter = SearchEventFilter(request.GET, queryset=get_event_qs(request).order_by("-start_day"))
paginator = Paginator(filter.qs, 10)
page = request.GET.get('page')
@ -444,4 +482,10 @@ def event_search(request):
except EmptyPage:
response = paginator.page(paginator.num_pages)
return render(request, 'agenda_culturel/search.html', {'filter': filter, 'paginator_filter': response})
return render(request, 'agenda_culturel/search.html', {'filter': filter,
'has_results': len(request.GET) != 0 or (len(request.GET) > 1 and "page" in request.GET),
'paginator_filter': response,
'full': full})
def event_search_full(request):
return event_search(request, True)