Amélioration de la recherche
This commit is contained in:
		@@ -298,9 +298,9 @@ article#filters {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.helptext {
 | 
			
		||||
.helptext, .location-search {
 | 
			
		||||
    font-size: 80%;
 | 
			
		||||
    opacity: 0.7;
 | 
			
		||||
    margin-top: -0.7em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.django-ckeditor-widget {
 | 
			
		||||
@@ -316,4 +316,16 @@ 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;
 | 
			
		||||
}
 | 
			
		||||
@@ -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">
 | 
			
		||||
        {% for field in filter.form %}
 | 
			
		||||
        <div>
 | 
			
		||||
            {{ field.errors }}
 | 
			
		||||
            {{ field.label_tag }} {{ field }}
 | 
			
		||||
        </div>
 | 
			
		||||
        {% if forloop.first %}
 | 
			
		||||
        <details>
 | 
			
		||||
            <summary>Rechercher avancée</summary>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
        {% if forloop.last %}
 | 
			
		||||
        </details>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
    <form method="get" class="form django-form" action="#results">
 | 
			
		||||
        {{ filter.form }}
 | 
			
		||||
        <button type="submit">Rechercher</button>
 | 
			
		||||
 | 
			
		||||
    <button type="submit">Rechercher</button>
 | 
			
		||||
        {% 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 %}
 | 
			
		||||
    </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">« 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 %}
 | 
			
		||||
                 : <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">« 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 »</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 »</a>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                </span>
 | 
			
		||||
            </footer>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
    </span>
 | 
			
		||||
</footer>
 | 
			
		||||
    </div>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
</article>
 | 
			
		||||
 | 
			
		||||
{% endblock %}
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user