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