@@ -5,8 +5,15 @@ import re
 | 
			
		||||
import django_filters
 | 
			
		||||
from django import forms
 | 
			
		||||
from django.contrib.gis.measure import D
 | 
			
		||||
from django.contrib.postgres.search import SearchHeadline, SearchQuery
 | 
			
		||||
from django.db.models import F, Q
 | 
			
		||||
from django.contrib.postgres.search import (
 | 
			
		||||
    SearchVector,
 | 
			
		||||
    SearchQuery,
 | 
			
		||||
    SearchRank,
 | 
			
		||||
    SearchHeadline,
 | 
			
		||||
    TrigramSimilarity,
 | 
			
		||||
)
 | 
			
		||||
from django.db.models import Q, F, Func
 | 
			
		||||
from django.db.models.functions import Greatest, Lower
 | 
			
		||||
from django.http import QueryDict
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
@@ -18,6 +25,7 @@ from .models import (
 | 
			
		||||
    RecurrentImport,
 | 
			
		||||
    ReferenceLocation,
 | 
			
		||||
    Tag,
 | 
			
		||||
    remove_accents,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -531,18 +539,27 @@ class SimpleSearchEventFilter(django_filters.FilterSet):
 | 
			
		||||
        return qs
 | 
			
		||||
 | 
			
		||||
    def custom_filter(self, queryset, name, value):
 | 
			
		||||
        value = remove_accents(value)
 | 
			
		||||
        search_query = SearchQuery(value, config="french")
 | 
			
		||||
        qs = queryset.filter(
 | 
			
		||||
            Q(title__icontains=value)
 | 
			
		||||
            | Q(category__name__icontains=value)
 | 
			
		||||
            | Q(tags__icontains=[value])
 | 
			
		||||
            | Q(exact_location__name__icontains=value)
 | 
			
		||||
            | Q(description__icontains=value)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        search_vector = SearchVector(
 | 
			
		||||
            "title", weight="A", config="french"
 | 
			
		||||
        ) + SearchVector("description", weight="B", config="french")
 | 
			
		||||
 | 
			
		||||
        # Full-text rank
 | 
			
		||||
        qs = queryset.annotate(
 | 
			
		||||
            rank=SearchRank(search_vector, search_query),
 | 
			
		||||
            similarity=Greatest(
 | 
			
		||||
                TrigramSimilarity(Func(Lower("title"), function="unaccent"), value),
 | 
			
		||||
                TrigramSimilarity(
 | 
			
		||||
                    Func(Lower("description"), function="unaccent"), value
 | 
			
		||||
                ),
 | 
			
		||||
            ),
 | 
			
		||||
            relevance=F("rank") + F("similarity"),
 | 
			
		||||
        ).filter(Q(rank__gte=0.5) | Q(similarity__gte=0.3))
 | 
			
		||||
 | 
			
		||||
        for f in [
 | 
			
		||||
            "title",
 | 
			
		||||
            "category__name",
 | 
			
		||||
            "exact_location__name",
 | 
			
		||||
            "description",
 | 
			
		||||
        ]:
 | 
			
		||||
            params = {
 | 
			
		||||
@@ -556,7 +573,7 @@ class SimpleSearchEventFilter(django_filters.FilterSet):
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            qs = qs.annotate(**params)
 | 
			
		||||
        return qs
 | 
			
		||||
        return qs.order_by("-relevance")
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Event
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,17 @@
 | 
			
		||||
{% if paginator_filter.paginator.num_pages != 1 %}
 | 
			
		||||
{% if paginator_filter.paginator.num_pages != 1 and paginator_filter|length > 0 %}
 | 
			
		||||
  <span>
 | 
			
		||||
    {% if paginator_filter.has_previous %}
 | 
			
		||||
      <a href="{{ paginator_filter.paginator.url_first_page }}" role="button">« premier</a>
 | 
			
		||||
      <a href="{{ paginator_filter.url_previous_page }}" role="button">précédent</a>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
    <span>Page {{ paginator_filter.number }} sur {{ paginator_filter.paginator.num_pages }}</span>
 | 
			
		||||
    <span>Page {{ paginator_filter.number }}
 | 
			
		||||
      {% if not nomax %}sur {{ paginator_filter.paginator.num_pages }}{% endif %}
 | 
			
		||||
    </span>
 | 
			
		||||
    {% if paginator_filter.has_next %}
 | 
			
		||||
      <a href="{{ paginator_filter.url_next_page }}" role="button">suivant</a>
 | 
			
		||||
      <a href="{{ paginator_filter.paginator.url_last_page }}" role="button">dernier »</a>
 | 
			
		||||
      {% if paginator_filter.count < 9999999998 %}
 | 
			
		||||
        <a href="{{ paginator_filter.paginator.url_last_page }}" role="button">dernier »</a>
 | 
			
		||||
      {% endif %}
 | 
			
		||||
    {% endif %}
 | 
			
		||||
  </span>
 | 
			
		||||
{% else %}
 | 
			
		||||
  Page 1 sur 1
 | 
			
		||||
{% endif %}
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@
 | 
			
		||||
        <a href="{% url 'event_search_full' %}">Recherche avancée  {% picto_from_name "chevron-right" %}</a>
 | 
			
		||||
      {% endif %}
 | 
			
		||||
    </form>
 | 
			
		||||
    {% if has_results or categories %}
 | 
			
		||||
    {% if has_results or categories or tags or places or organisations or rimports %}
 | 
			
		||||
      <div id="results">
 | 
			
		||||
        {% if categories %}
 | 
			
		||||
          <div class="message success">
 | 
			
		||||
@@ -87,16 +87,13 @@
 | 
			
		||||
            {% endfor %}
 | 
			
		||||
          </div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
        <p>
 | 
			
		||||
          {{ paginator_filter.paginator.count }} événement{{ paginator_filter.object_list.count | pluralize }} correspond{{ paginator_filter.object_list.count | pluralize:"ent" }} à la recherche.
 | 
			
		||||
        </p>
 | 
			
		||||
        <div>
 | 
			
		||||
          {% for obj in paginator_filter %}
 | 
			
		||||
            {% include "agenda_culturel/single-event/event-in-flat-list-inc.html" with event=obj %}
 | 
			
		||||
          {% endfor %}
 | 
			
		||||
        </div>
 | 
			
		||||
        <footer>
 | 
			
		||||
          {% include "agenda_culturel/paginator_filter.html" %}
 | 
			
		||||
          {% include "agenda_culturel/paginator_filter.html" with nomax=1 %}
 | 
			
		||||
        </footer>
 | 
			
		||||
      </div>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@ import emoji
 | 
			
		||||
from django.core.paginator import PageNotAnInteger, EmptyPage
 | 
			
		||||
from django.shortcuts import render
 | 
			
		||||
 | 
			
		||||
from . import PaginatorFilter
 | 
			
		||||
from ..filters import SearchEventFilter, SimpleSearchEventFilter
 | 
			
		||||
from ..models import (
 | 
			
		||||
    Category,
 | 
			
		||||
@@ -17,6 +16,8 @@ from django.db.models import Q, F, Func
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def event_search(request, full=False):
 | 
			
		||||
    from .utils import PaginatorFilterCountless
 | 
			
		||||
 | 
			
		||||
    categories = None
 | 
			
		||||
    tags = None
 | 
			
		||||
    places = None
 | 
			
		||||
@@ -79,7 +80,7 @@ def event_search(request, full=False):
 | 
			
		||||
                    name__icontains=request.GET["q"]
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
    paginator = PaginatorFilter(filter, 10, request)
 | 
			
		||||
    paginator = PaginatorFilterCountless(filter, 10, request)
 | 
			
		||||
    page = request.GET.get("page")
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
from django.core.paginator import EmptyPage, Paginator
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
from django.utils.functional import cached_property
 | 
			
		||||
 | 
			
		||||
from django.db.models import Aggregate, FloatField
 | 
			
		||||
from ..models import Event
 | 
			
		||||
@@ -75,3 +76,10 @@ class PaginatorFilter(Paginator):
 | 
			
		||||
            page.url_next_page = self.request.get_full_path()
 | 
			
		||||
 | 
			
		||||
        return page
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PaginatorFilterCountless(PaginatorFilter):
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def count(self):
 | 
			
		||||
        return 9999999999
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user