Replace Haystack by django-elasticsearch-dsl. Haystack is compatible with an outdated version of elasticsearch
--- a/src/iconolab/apps.py Wed May 16 00:22:05 2018 +0200
+++ b/src/iconolab/apps.py Tue Jun 05 11:32:49 2018 +0200
@@ -7,3 +7,4 @@
def ready(self):
import iconolab.signals.handlers
import iconolab.templatetags.iconolab_tags
+ from iconolab.conf import settings
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/conf.py Tue Jun 05 11:32:49 2018 +0200
@@ -0,0 +1,58 @@
+from django.conf import settings
+from appconf import AppConf
+from elasticsearch_dsl import analyzer, token_filter
+
+french_elision = token_filter(
+ 'french_elision',
+ type='elision',
+ articles_case=True,
+ articles=["l", "m", "t", "qu", "n", "s", "j", "d", "c", "jusqu", "quoiqu", "lorsqu", "puisqu"]
+)
+french_stemmer = token_filter(
+ 'french_stemmer',
+ type="stemmer",
+ language="light_french"
+)
+french_analyzer = analyzer(
+ 'french',
+ tokenizer='standard',
+ filter=['lowercase', 'asciifolding', french_elision, french_stemmer]
+)
+
+class IconolabConf(AppConf):
+
+
+ INDEXES_CONFIG = {
+ 'items': {
+ 'name': 'iconolab_items',
+ 'number_of_shards': 1,
+ 'number_of_replicas': 0,
+ },
+ 'annotations': {
+ 'name': 'iconolab_annotations',
+ 'number_of_shards': 1,
+ 'number_of_replicas': 0,
+ }
+ }
+
+ INDEXES_DEFAULT_CONFIG = {
+ 'number_of_shards': 1,
+ 'number_of_replicas': 0
+ }
+
+ INDEXES_ANALYZER = {
+ 'items': french_analyzer,
+ 'annotations': french_analyzer
+ }
+
+ INDEXES_FIELD_ANALYZER = {
+ 'items': french_analyzer,
+ 'annotations': french_analyzer
+ }
+
+ INDEXES_QUERYSET_PAGINATION = 5000
+
+ SEARCH_PAGE_SIZE = 10
+
+ class Meta:
+ prefix = 'iconolab'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/iconolab/documents.py Tue Jun 05 11:32:49 2018 +0200
@@ -0,0 +1,3 @@
+from .search_indexes import AnnotationDocument, ItemDocument
+
+__all__ = ['AnnotationDocument', 'ItemDocument']
--- a/src/iconolab/models.py Wed May 16 00:22:05 2018 +0200
+++ b/src/iconolab/models.py Tue Jun 05 11:32:49 2018 +0200
@@ -472,6 +472,8 @@
@property
def tag_labels(self):
+ if self.current_revision is None:
+ return []
current_revision_tags = json.loads(
self.current_revision.get_tags_json())
return [tag_infos['tag_label'] for tag_infos in current_revision_tags if tag_infos.get('tag_label') is not None]
--- a/src/iconolab/search_indexes/__init__.py Wed May 16 00:22:05 2018 +0200
+++ b/src/iconolab/search_indexes/__init__.py Tue Jun 05 11:32:49 2018 +0200
@@ -1,2 +1,3 @@
-from .indexes import AnnotationIndex, ItemIndex
-__all__ = ['AnnotationIndex', 'ItemIndex', 'RevisionSignalProcessor']
\ No newline at end of file
+# from .indexes import AnnotationIndex, ItemIndex
+from .indexes import ItemDocument, AnnotationDocument
+__all__ = ['AnnotationDocument', 'ItemDocument']
--- a/src/iconolab/search_indexes/forms.py Wed May 16 00:22:05 2018 +0200
+++ b/src/iconolab/search_indexes/forms.py Tue Jun 05 11:32:49 2018 +0200
@@ -1,19 +1,28 @@
+import json
+import logging
+
from django import forms
-from haystack.forms import SearchForm
-from iconolab.models import Item, Annotation, Collection
+from django.utils.translation import ugettext_lazy as _
+from iconolab.conf import settings
+from iconolab.models import Annotation, Collection, Item
+from .indexes import AnnotationDocument, ItemDocument
+from .query import EmptyQueryResults, QueryResults
+logger = logging.getLogger(__name__)
-class IconolabSearchForm(SearchForm):
-
+class IconolabSearchForm(forms.Form):
+ """
+ Inspired by Haystack.forms.SearchForm
+ """
+ q = forms.CharField(required=False, label=_('Search'),
+ widget=forms.TextInput(attrs={'type': 'search'}))
model_type = forms.ChoiceField(required=False, choices=(("images","Images"), ("annotations","Annotations")) )
tags = forms.BooleanField(required=False, initial=False)
def __init__(self, *args, **kwargs):
self.collection_name = kwargs.pop("collection_name")
- if self.collection_name and Collection.objects.filter(name=self.collection_name).exists():
- self.collection = Collection.objects.get(name=self.collection_name)
selected_model_type = kwargs.pop("model_type", None)
if selected_model_type is not None:
@@ -23,37 +32,40 @@
data["model_type"] = selected_model_type
kwargs['data'] = data
- super(IconolabSearchForm, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
+
+
+ def search(self):
+
+ model_type = self.cleaned_data.get('model_type')
+
+ model_klass = None
+ document_klass = None
+ refine_qs = lambda qs: qs
- def no_query_found(self):
- # load all
- selected_type = self.cleaned_data.get("model_type")
- qs = self.get_model_type_queryset(self.searchqueryset, selected_type).load_all()
- return qs
+ if(model_type == 'images'):
+ model_klass = Item
+ document_klass = ItemDocument
+ refine_qs = lambda qs: qs.select_related('collection', 'metadatas')
+ elif(model_type == 'annotations'):
+ model_klass = Annotation
+ document_klass = AnnotationDocument
+ refine_qs = lambda qs: qs.select_related('image', 'image__item', 'image__item__collection', 'stats', 'current_revision', 'author')
- def get_model_type_queryset(self, qs, model_type, tags_only):
+ q = self.cleaned_data.get('q','')
+ if model_klass is None or not q:
+ return EmptyQueryResults()
- if model_type == 'images':
- qs = qs.models(Item).load_all_queryset(Item, Item.objects.select_related('collection', 'metadatas'))
- if model_type == 'annotations':
- qs = qs.models(Annotation).load_all_queryset(Annotation, Annotation.objects.select_related('image', 'image__item', 'image__item__collection', 'stats', 'current_revision', 'author'))
+ tags_only = self.cleaned_data.get("tags", False)
+ fields = ['text'] if not tags_only else ['tags']
+
+ el_qs = document_klass.search()
if self.collection_name is not None:
- qs = qs.filter(collection = self.collection_name)
+ el_qs = el_qs.filter('term', collection=self.collection_name)
- if tags_only:
- qs = qs.filter(tags=self.cleaned_data.get("q"))
-
- return qs
+ el_qs = el_qs.query('simple_query_string', query=self.cleaned_data.get('q'), fields=fields)
- def search(self):
- selected_type = self.cleaned_data.get("model_type")
- tags_only = self.cleaned_data.get("tags")
-
- qs = super(IconolabSearchForm, self).search()
+ logger.debug("SEARCH : %s", json.dumps(el_qs.to_dict()))
- if qs.count() == 0:
- return qs
- else:
- qs = self.get_model_type_queryset(qs, selected_type, tags_only).load_all()
- return qs
+ return QueryResults(el_qs, refine_qs)
--- a/src/iconolab/search_indexes/indexes.py Wed May 16 00:22:05 2018 +0200
+++ b/src/iconolab/search_indexes/indexes.py Tue Jun 05 11:32:49 2018 +0200
@@ -1,42 +1,99 @@
+from django.template.loader import render_to_string
from django.utils import timezone
-from haystack import indexes
-from iconolab.models import Annotation, Item
+
+from django_elasticsearch_dsl import DocType, Index, fields
+from iconolab.conf import settings
+from iconolab.models import (Annotation, AnnotationRevision, Collection, Item,
+ ItemMetadata)
+
+__all__ = ['ItemDocument', 'AnnotationDocument']
-class ItemIndex(indexes.SearchIndex, indexes.Indexable):
- text = indexes.CharField(document=True, use_template=True)
+def build_index(key):
+ index_config = settings.ICONOLAB_INDEXES_CONFIG.get(key) or settings.ICONOLAB_INDEXES_DEFAULT_CONFIG
+ index_name = index_config.pop('name', 'iconolab_' + key)
+ index = Index(index_name)
+ index.settings(**index_config)
+ if settings.ICONOLAB_INDEXES_ANALYZER.get(key) is not None:
+ index.analyzer(settings.ICONOLAB_INDEXES_ANALYZER.get(key))
+ return index
- collection = indexes.CharField(model_attr="collection")
- authors = indexes.CharField(model_attr="metadatas__authors")
- school = indexes.CharField(model_attr="metadatas__school")
- designation = indexes.CharField(model_attr="metadatas__designation")
- datation = indexes.CharField(model_attr="metadatas__datation")
- technics = indexes.CharField(model_attr="metadatas__technics")
- measurements = indexes.CharField(model_attr="metadatas__measurements")
- create_or_usage_location = indexes.CharField(model_attr="metadatas__create_or_usage_location")
- discovery_context = indexes.CharField(model_attr="metadatas__discovery_context")
- conservation_location = indexes.CharField(model_attr="metadatas__conservation_location")
-
- #tags = indexes.MultiValueField(model_attr="tag_labels")
+def get_text_field_kwargs(key):
+ analyzer = settings.ICONOLAB_INDEXES_FIELD_ANALYZER.get(key)
+ if analyzer is not None:
+ return {
+ 'analyzer': analyzer
+ }
+ else:
+ return {}
+
- def get_model(self):
- return Item
+items_index = build_index('items')
- def index_queryset(self, using=None):
- return self.get_model().objects.filter()
+@items_index.doc_type
+class ItemDocument(DocType):
+ class Meta:
+ model = Item
+
+ queryset_pagination = settings.ICONOLAB_INDEXES_QUERYSET_PAGINATION
+ related_models = [ItemMetadata]
-class AnnotationIndex(indexes.SearchIndex, indexes.Indexable):
+ text = fields.TextField(**get_text_field_kwargs('items'))
+
+ collection = fields.KeywordField(attr="collection.name")
+
+ authors = fields.TextField(attr="metadatas.authors")
+ school = fields.TextField(attr="metadatas.school")
+ designation = fields.TextField(attr="metadatas.designation")
+ datation = fields.TextField(attr="metadatas.datation")
+ technics = fields.TextField(attr="metadatas.technics")
+ measurements = fields.TextField(attr="metadatas.measurements")
+ create_or_usage_location = fields.TextField(attr="metadatas.create_or_usage_location")
+ discovery_context = fields.TextField(attr="metadatas.discovery_context")
+ conservation_location = fields.TextField(attr="metadatas.conservation_location")
+
+ def prepare_text(self, instance):
+ return render_to_string('search/indexes/iconolab/item_text.txt', { 'object': instance })
+
+ def get_queryset(self):
+ return super().get_queryset().select_related('collection', 'metadatas').order_by('id')
+
+ def get_instances_from_related(self, related_instance):
+ if isinstance(related_instance, ItemMetadata):
+ return related_instance.item
+ else:
+ return None
+
+
+
+annotations_index = build_index('annotations')
- ##indexed field
- text = indexes.CharField(document=True, use_template=True)
- title = indexes.CharField(model_attr="current_revision__title")
- description = indexes.CharField(model_attr="current_revision__description")
- collection = indexes.CharField(model_attr="image__item__collection")
- tags = indexes.MultiValueField(model_attr="tag_labels")
-
- ## tags
- def get_model(self):
- return Annotation
+@annotations_index.doc_type
+class AnnotationDocument(DocType):
+
+ class Meta:
+ model = Annotation
+
+ queryset_pagination = settings.ICONOLAB_INDEXES_QUERYSET_PAGINATION
+ related_models = [ AnnotationRevision ]
+
+
+ text = fields.TextField(**get_text_field_kwargs('annotations'))
- def index_queryset(self, using=None):
- return self.get_model().objects.filter(created__lte=timezone.now())
\ No newline at end of file
+ title = fields.TextField(attr="current_revision.title")
+ description = fields.TextField(attr="current_revision.description")
+ collection = fields.KeywordField(attr="image.item.collection.name")
+ tags = fields.TextField(attr="tag_labels", multi=True)
+
+ def prepare_text(self, instance):
+ return render_to_string('search/indexes/iconolab/annotation_text.txt', { 'object': instance })
+
+ def get_queryset(self):
+ return super().get_queryset().filter(created__lte=timezone.now()).select_related('current_revision', 'image__item__collection').order_by('id')
+
+ def get_instances_from_related(self, related_instance):
+ if isinstance(related_instance, AnnotationRevision):
+ return related_instance.annotation
+ else:
+ return None
+
--- a/src/iconolab/search_indexes/query.py Wed May 16 00:22:05 2018 +0200
+++ b/src/iconolab/search_indexes/query.py Tue Jun 05 11:32:49 2018 +0200
@@ -1,17 +1,36 @@
-from haystack.query import RelatedSearchQuerySet
-from iconolab.models import Annotation, Item
-from pprint import pprint
+from django.utils.functional import LazyObject
+from itertools import starmap
+
+__all__ = ['QueryResults', 'EmptyQueryResults']
-class IconolabRelatedQuerySet(RelatedSearchQuerySet):
- def __init__(self, using=None, query=None):
- super(IconolabRelatedQuerySet, self).__init__(using=using, query=query)
+def add_obj(result, instance):
+ result.object = instance
+ return result
+
+class EmptyQueryResults(object):
+ def __len__(self):
+ return 0
+
+ def __getitem__(self, index):
+ return []
- def in_bulk(self, ids):
- results = {}
- int_ids = [ int(id) for id in ids]
- annotations = Annotation.objects.filter(pk__in = int_ids)
+class QueryResults(LazyObject):
+ def __init__(self, search_results, refine_query):
+ self._wrapped = search_results
+ self.refine_query = refine_query
+
+ def _setup(self):
+ # do nothing
+ pass
- for annotation in annotations:
- results[annotation.pk] = annotation
+ def __len__(self):
+ return self._wrapped.count()
- return results
+ def __getitem__(self, index):
+ sliced_search_results = self._wrapped[index]
+ if isinstance(index, slice):
+ sliced_search_results = starmap(
+ add_obj,
+ zip(sliced_search_results, self.refine_query(sliced_search_results.to_queryset()))
+ )
+ return sliced_search_results
--- a/src/iconolab/search_indexes/signals.py Wed May 16 00:22:05 2018 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,26 +0,0 @@
-from iconolab.models import Image, AnnotationRevision, Annotation
-from haystack import signals
-from django.db import models
-from iconolab.signals.handlers import revision_created, revision_accepted
-import logging
-
-logger = logging.getLogger(__name__)
-
-# update / create new index when a new revision is accepted
-# Then update images tags related to this revision
-class RevisionSignalProcessor(signals.BaseSignalProcessor):
-
- def setup(self):
- revision_created.connect(self.handle_revision, sender=AnnotationRevision)
- revision_accepted.connect(self.handle_revision, sender=AnnotationRevision)
-
- def handle_revision(self, **kwargs):
- revision_instance = kwargs.get("instance", None)
- if revision_instance and revision_instance.state in [AnnotationRevision.ACCEPTED]:
- annotation = revision_instance.annotation
- image_annotation = revision_instance.annotation.image
- self.handle_save(Annotation, annotation)
- self.handle_save(Image, image_annotation)##useful for tag
-
- def teardown(self):
- revision_accepted.disconnect(self.handle_accepted_revision, sender=AnnotationRevision)
--- a/src/iconolab/search_indexes/urls.py Wed May 16 00:22:05 2018 +0200
+++ b/src/iconolab/search_indexes/urls.py Tue Jun 05 11:32:49 2018 +0200
@@ -6,9 +6,9 @@
app_name = "iconolab-search_indexes"
urlpatterns = [
url(r'collection/(?P<collection_name>[a-z0-9\-]+)/model/(?P<model_type>[a-z0-9\-]+)', views.IconolabSearchView.as_view(), name="collection_with_model_search"),
- url(r'collection/(?P<collection_name>[a-z0-9\-]+)', views.IconolabSearchView.as_view(), name="collection_haystack_search"),
+ url(r'collection/(?P<collection_name>[a-z0-9\-]+)', views.IconolabSearchView.as_view(), name="collection_search"),
url(r'^model/(?P<model_type>[a-z0-9\-]+)', views.IconolabSearchView.as_view(), name="model_search"),
- url(r'^global/$', views.IconolabSearchView.as_view(), name="haystack_search"),
+ url(r'^global/$', views.IconolabSearchView.as_view(), name="global_search"),
]
--- a/src/iconolab/search_indexes/views.py Wed May 16 00:22:05 2018 +0200
+++ b/src/iconolab/search_indexes/views.py Tue Jun 05 11:32:49 2018 +0200
@@ -1,22 +1,34 @@
-from haystack.generic_views import SearchView
-from haystack.query import RelatedSearchQuerySet
-from iconolab.search_indexes.forms import IconolabSearchForm
from django.shortcuts import HttpResponse, redirect
from django.urls import reverse
-from django.views.generic import RedirectView
+from django.views.generic import ListView, RedirectView
+from django.views.generic.edit import FormMixin
+
+from iconolab.conf import settings
from iconolab.models import Collection
+from iconolab.search_indexes.forms import IconolabSearchForm
-#override Search and Related QuerySet here
-class IconolabSearchView(SearchView):
- form_class = IconolabSearchForm
- queryset = RelatedSearchQuerySet()
+import logging
+
+logger = logging.getLogger(__name__)
+
+
+class IconolabSearchView(ListView):
+
template_name = "search/default_search.html"
- load_all = True
-
templates_map = {
"images": "search/image_search.html",
"annotations": "search/annotation_search.html"
}
+ paginate_by = settings.ICONOLAB_SEARCH_PAGE_SIZE
+
+ def get_template_names(self):
+ try:
+ model_type = self.kwargs.get('model_type', None)
+ template = IconolabSearchView.templates_map[model_type]
+ except KeyError:
+ template = IconolabSearchView.template_name
+ finally:
+ return [template]
def complete_url(self, url, tags_only):
query = self.request.GET.get("q", None)
@@ -43,8 +55,9 @@
def get(self, request, *args, **kwargs):
self.model_type = request.GET.get('model_type', None)
- self.paginate_by = request.GET.get('perpage', 10)
- collection_name = self.kwargs.get('collection_name', None)
+ self.paginate_by = request.GET.get(
+ 'perpage', settings.ICONOLAB_SEARCH_PAGE_SIZE)
+ self.collection_name = self.kwargs.get('collection_name', None)
tags_only = False
if (self.model_type == 'tags'):
@@ -52,20 +65,31 @@
tags_only = True
if self.model_type is not None:
- if collection_name is None:
- #redirect to all_model_type
- redirect_url = reverse('search_indexes:model_search', kwargs={'model_type': self.model_type})
+ if self.collection_name is None:
+ # redirect to all_model_type
+ redirect_url = reverse('search_indexes:model_search', kwargs={
+ 'model_type': self.model_type})
return redirect(self.complete_url(redirect_url, tags_only))
else:
- redirect_url = reverse('search_indexes:collection_with_model_search', kwargs={'collection_name': collection_name, 'model_type':self.model_type})
+ redirect_url = reverse('search_indexes:collection_with_model_search', kwargs={
+ 'collection_name': self.collection_name, 'model_type': self.model_type})
return redirect(self.complete_url(redirect_url, tags_only))
else:
has_error, redirectView = self.check_kwargs(**kwargs)
if has_error:
return redirectView(request)
- return super(IconolabSearchView, self).get(request, *args, **kwargs)
+ return super().get(request, *args, **kwargs)
+
def check_kwargs(self, **kwargs):
+ self.collection = None
+ if self.collection_name:
+ self.collection = Collection.objects.filter(name=self.collection_name).first()
+ if not self.collection_name or self.collection:
+ return (False, None)
+ else:
+ return (True, RedirectView.as_view(url=reverse('404error')))
+
result = (False, None)
try:
collection_name = kwargs.get('collection_name', None)
@@ -77,25 +101,31 @@
finally:
return result
- def get_queryset(self):
- return IconolabSearchView.queryset
+
+ def get_form(self, initial=None):
- def get_form_kwargs(self):
- kwargs = super(IconolabSearchView, self).get_form_kwargs()
- kwargs['collection_name'] = self.kwargs.get('collection_name', None)
- kwargs['model_type'] = self.kwargs.get('model_type', None)
- return kwargs
+ form_kwargs = {
+ 'collection_name': self.kwargs.get('collection_name', None),
+ 'model_type': self.kwargs.get('model_type', None)
+ }
- def get_template_names(self):
- try :
- model_type = self.kwargs.get('model_type', None)
- template = IconolabSearchView.templates_map[model_type]
- except KeyError:
- template = IconolabSearchView.template_name
- finally:
- return [template]
+ if initial is not None:
+ form_kwargs['initial'] : initial
+ else:
+ form_kwargs['data'] = self.request.GET
+ return IconolabSearchForm(**form_kwargs)
+
+ def get_queryset(self):
+ form = self.get_form()
+ if form.is_valid():
+ return form.search()
+ else:
+ return []
def get_context_data(self, *args, **kwargs):
- context = super(IconolabSearchView, self).get_context_data(*args, **kwargs)
- context['collection_name'] = self.kwargs.get('collection_name', '')
+ context = super().get_context_data(*args, **kwargs)
+ context['collection_name'] = self.collection_name
+ context['collection'] = self.collection
+ context['form'] = self.get_form(initial=self.request.GET)
+
return context
--- a/src/iconolab/templates/partials/header_search_form.html Wed May 16 00:22:05 2018 +0200
+++ b/src/iconolab/templates/partials/header_search_form.html Tue Jun 05 11:32:49 2018 +0200
@@ -1,8 +1,8 @@
{% if collection_name %}
-<form class="navbar-form navbar-left" method="GET" action="{% url 'search_indexes:collection_haystack_search' collection_name %}" role="search">
+<form class="navbar-form navbar-left" method="GET" action="{% url 'search_indexes:collection_search' collection_name %}" role="search">
{% else %}
-<form class="navbar-form navbar-left" method="GET" action="{% url 'search_indexes:haystack_search' %}" role="search">
+<form class="navbar-form navbar-left" method="GET" action="{% url 'search_indexes:global_search' %}" role="search">
{% endif %}
<div class="form-group">
<input name="q" type="text" class="form-control" placeholder="Trouver ...">
--- a/src/iconolab/templates/search/annotation_search.html Wed May 16 00:22:05 2018 +0200
+++ b/src/iconolab/templates/search/annotation_search.html Tue Jun 05 11:32:49 2018 +0200
@@ -1,13 +1,13 @@
{% extends 'iconolab_base.html' %}
{% load thumbnail %}
-{% load iconolab_tags %}
+{% load iconolab_tags %}
{% block content %}
<h2>Recherche</h2>
- <form method="get" class="form-inline" action="{% if collection_name %}{% url 'search_indexes:collection_haystack_search' collection_name %}{% else %}{% url 'search_indexes:haystack_search' %}{% endif %}">
+ <form method="get" class="form-inline" action="{% if collection_name %}{% url 'search_indexes:collection_search' collection_name %}{% else %}{% url 'search_indexes:global_search' %}{% endif %}">
<div class="form-group">
<div class="input-group">
<div class="input-group-addon">Chercher</div>
@@ -18,8 +18,8 @@
<option value="{{ val }}"{% if label == 'Annotations' %} selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
- {% if form.collection_name %}
- <div class="input-group-addon">du fonds {{form.collection.verbose_name}}</div>
+ {% if collection %}
+ <div class="input-group-addon">du fonds {{collection.verbose_name}}</div>
{% else %}
<div class="input-group-addon">du site</div>
{% endif %}
@@ -27,7 +27,7 @@
</div>
<input type="submit" class="search-submit btn btn-primary" value="Rechercher" style="margin-left: 10px;">
</form>
- <h3><strong>{{ page_obj.paginator.count }}</strong> annotation(s)</h3>
+ <h3><strong>{{ page_obj.paginator.count }}</strong> annotation(s)</h3>
<ul class="annotation-list-wrapper list-inline">
{% if not page_obj.object_list %}
<h3 class="text-center"><small>Aucune annotation à afficher</small></p>
@@ -68,7 +68,7 @@
{% endfor %}
</td>
<td>
- {% include "partials/annotation_stats_panel.html" with annotation=result.object %}
+ {% include "partials/annotation_stats_panel.html" with annotation=result.object %}
</td>
</tr>
{% endfor %}
@@ -78,16 +78,16 @@
<ul class="pagination pull-right items-perpage" style="margin-left: 15px;">
<li class="active pagination-label"><a>Annotations par page : </a></li>
<li class="{% if page_obj.paginator.per_page == 5 %}active{% endif %}">
- <a href="?q={{ query }}&perpage=5">5</a>
+ <a href="?q={{ query }}&perpage=5">5</a>
</li>
<li class="{% if page_obj.paginator.per_page == 10 %}active{% endif %}">
- <a href="?q={{ query }}&perpage=10">10</a>
+ <a href="?q={{ query }}&perpage=10">10</a>
</li>
<li class="{% if page_obj.paginator.per_page == 25 %}active{% endif %}">
- <a href="?q={{ query }}&perpage=25">25</a>
+ <a href="?q={{ query }}&perpage=25">25</a>
</li>
<li class="{% if page_obj.paginator.per_page == 100 %}active{% endif %}">
- <a href="?q={{ query }}&perpage=100">100</a>
+ <a href="?q={{ query }}&perpage=100">100</a>
</li>
</ul>
{% if page_obj.has_previous or page_obj.has_next %}
@@ -99,13 +99,13 @@
</a>
</li>
{% endif %}
-
+
{% for page in page_obj.paginator.page_range %}
<li id="page-link-{{page}}" class="pagination-link {% if page == page_obj.number %}active{% endif %}">
<a {% if page != page_obj.number %}href="?q={{query}}&page={{page}}&perpage={{page_obj.paginator.per_page}}"{% endif %}>{{page}}</a>
</li>
{% endfor %}
-
+
{% if page_obj.has_next %}
<li>
<a href="?page={{page_obj.next_page_number}}&perpage={{page_obj.paginator.per_page}}" aria-label="Précédent">
@@ -115,4 +115,4 @@
{% endif %}
</ul>
{% endif %}
-{% endblock %}
\ No newline at end of file
+{% endblock %}
--- a/src/iconolab/templates/search/default_search.html Wed May 16 00:22:05 2018 +0200
+++ b/src/iconolab/templates/search/default_search.html Tue Jun 05 11:32:49 2018 +0200
@@ -1,13 +1,13 @@
{% extends 'iconolab_base.html' %}
{% load thumbnail %}
-{% load iconolab_tags %}
+{% load iconolab_tags %}
{% block content %}
<h2>Recherche</h2>
- <form method="get" class="form-inline" action="{% url 'search_indexes:haystack_search' %}">
+ <form method="get" class="form-inline" action="{% url 'search_indexes:global_search' %}">
<div class="form-group">
<div class="input-group">
<div class="input-group-addon">Chercher</div>
@@ -18,8 +18,8 @@
<option value="{{ val }}"{% if label == 'Images' %} selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
- {% if form.collection_name %}
- <div class="input-group-addon">du fonds {{form.collection.verbose_name}}</div>
+ {% if collection %}
+ <div class="input-group-addon">du fonds {{collection.verbose_name}}</div>
{% else %}
<div class="input-group-addon">du site</div>
{% endif %}
@@ -27,4 +27,4 @@
</div>
<input type="submit" class="search-submit btn btn-primary" value="Rechercher" style="margin-left: 10px;">
</form>>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
--- a/src/iconolab/templates/search/image_search.html Wed May 16 00:22:05 2018 +0200
+++ b/src/iconolab/templates/search/image_search.html Tue Jun 05 11:32:49 2018 +0200
@@ -1,13 +1,13 @@
{% extends 'iconolab_base.html' %}
{% load thumbnail %}
-{% load iconolab_tags %}
+{% load iconolab_tags %}
{% block content %}
<h2>Recherche</h2>
- <form method="get" class="form-inline" action="{% if collection_name %}{% url 'search_indexes:collection_haystack_search' collection_name %}{% else %}{% url 'search_indexes:haystack_search' %}{% endif %}">
+ <form method="get" class="form-inline" action="{% if collection_name %}{% url 'search_indexes:collection_search' collection_name %}{% else %}{% url 'search_indexes:global_search' %}{% endif %}">
<div class="form-group">
<div class="input-group">
<div class="input-group-addon">Chercher</div>
@@ -18,8 +18,8 @@
<option value="{{ val }}"{% if label == 'Images' %} selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
- {% if form.collection_name %}
- <div class="input-group-addon">du fonds {{form.collection.verbose_name}}</div>
+ {% if collection %}
+ <div class="input-group-addon">du fonds {{collection.verbose_name}}</div>
{% else %}
<div class="input-group-addon">du site</div>
{% endif %}
@@ -56,16 +56,16 @@
<ul class="pagination pull-right items-perpage" style="margin-left: 15px;">
<li class="active pagination-label"><a>Objets par page : </a></li>
<li class="{% if page_obj.paginator.per_page == 5 %}active{% endif %}">
- <a href="?q={{ query }}&perpage=5">5</a>
+ <a href="?q={{ query }}&perpage=5">5</a>
</li>
<li class="{% if page_obj.paginator.per_page == 10 %}active{% endif %}">
- <a href="?q={{ query }}&perpage=10">10</a>
+ <a href="?q={{ query }}&perpage=10">10</a>
</li>
<li class="{% if page_obj.paginator.per_page == 25 %}active{% endif %}">
- <a href="?q={{ query }}&perpage=25">25</a>
+ <a href="?q={{ query }}&perpage=25">25</a>
</li>
<li class="{% if page_obj.paginator.per_page == 100 %}active{% endif %}">
- <a href="?q={{ query }}&perpage=100">100</a>
+ <a href="?q={{ query }}&perpage=100">100</a>
</li>
</ul>
{% if page_obj.has_previous or page_obj.has_next %}
@@ -77,13 +77,13 @@
</a>
</li>
{% endif %}
-
+
{% for page in page_obj.paginator.page_range %}
<li id="page-link-{{page}}" class="pagination-link {% if page == page_obj.number %}active{% endif %}">
<a {% if page != page_obj.number %}href="?q={{query}}&page={{page}}&perpage={{page_obj.paginator.per_page}}"{% endif %}>{{page}}</a>
</li>
{% endfor %}
-
+
{% if page_obj.has_next %}
<li>
<a href="?page={{page_obj.next_page_number}}&perpage={{page_obj.paginator.per_page}}" aria-label="Précédent">
@@ -95,4 +95,4 @@
{% endif %}
</div>
</div>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
--- a/src/setup.py Wed May 16 00:22:05 2018 +0200
+++ b/src/setup.py Tue Jun 05 11:32:49 2018 +0200
@@ -123,9 +123,11 @@
setup_requires=['setuptools_scm'],
install_requires=[
"Django >= 2.0",
+ "django-appconf",
"django-comments-xtd",
"django-contrib-comments",
- "django-haystack",
+ "elasticsearch-dsl >= 6.0, < 7.0",
+ "django-elasticsearch-dsl",
"django-notifications-hq",
"elasticsearch",
"jsonfield",