implementation for searching something in 'News' and 'Intern'
This commit is contained in:
10
Readme.md
10
Readme.md
@@ -32,3 +32,13 @@ Mittels diesen Command können die Thumbs für die Alben in der Galerie erstellt
|
|||||||
<code>
|
<code>
|
||||||
python3 fet2020/manage.py create_thumbs
|
python3 fet2020/manage.py create_thumbs
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
Erstellt alle Searchindexes neu.
|
||||||
|
<code>
|
||||||
|
python3 fet2020/manage.py rebuild_index
|
||||||
|
</code>
|
||||||
|
|
||||||
|
Aktualisiert alle Searchindexes, die in den letzten 24 Stunden geändert wurden.
|
||||||
|
<code>
|
||||||
|
python3 fet2020/manage.py update_index --age=24
|
||||||
|
</code>
|
||||||
|
|||||||
@@ -77,6 +77,8 @@ INSTALLED_APPS = [
|
|||||||
"django_filters",
|
"django_filters",
|
||||||
"django_static_jquery_ui",
|
"django_static_jquery_ui",
|
||||||
"fontawesomefree",
|
"fontawesomefree",
|
||||||
|
"whoosh",
|
||||||
|
"haystack",
|
||||||
|
|
||||||
"core.apps.CoreConfig",
|
"core.apps.CoreConfig",
|
||||||
"posts.apps.PostsConfig",
|
"posts.apps.PostsConfig",
|
||||||
@@ -344,3 +346,12 @@ GALLERY = {
|
|||||||
CSRF_TRUSTED_ORIGINS = [
|
CSRF_TRUSTED_ORIGINS = [
|
||||||
"https://" + env("HOST_NAME"),
|
"https://" + env("HOST_NAME"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# DJANGO HAYSTACK
|
||||||
|
HAYSTACK_CONNECTIONS = {
|
||||||
|
"default": {
|
||||||
|
"ENGINE": "haystack.backends.whoosh_backend.WhooshEngine",
|
||||||
|
"PATH": os.path.join(BASE_DIR, "whoosh_index"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ urlpatterns = [
|
|||||||
path("members/", include("members.urls"), name="members"),
|
path("members/", include("members.urls"), name="members"),
|
||||||
path("member/", include(member_urlpatterns), name="member"),
|
path("member/", include(member_urlpatterns), name="member"),
|
||||||
path("posts/", include("posts.urls")),
|
path("posts/", include("posts.urls")),
|
||||||
|
path("search/", include("search.urls")),
|
||||||
path("tasks/", include("tasks.urls"), name="tasks"),
|
path("tasks/", include("tasks.urls"), name="tasks"),
|
||||||
path("intern/", include("intern.urls"), name="intern"),
|
path("intern/", include("intern.urls"), name="intern"),
|
||||||
path(
|
path(
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ from django.utils import timezone
|
|||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from documents import create_pad
|
from documents import create_pad, get_pad_html
|
||||||
|
from documents.api import get_pad_link
|
||||||
from tasks.models import TaskList
|
from tasks.models import TaskList
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -139,6 +140,12 @@ class Etherpad(models.Model):
|
|||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.title
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return get_pad_link(self.__get_pad_name())
|
||||||
|
|
||||||
def __get_pad_name(self):
|
def __get_pad_name(self):
|
||||||
return (
|
return (
|
||||||
slugify(self.date)
|
slugify(self.date)
|
||||||
@@ -170,8 +177,12 @@ class Etherpad(models.Model):
|
|||||||
_(f"Etherpad '{pad_name}' konnte nicht erstellt werden."),
|
_(f"Etherpad '{pad_name}' konnte nicht erstellt werden."),
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
@property
|
||||||
return self.title
|
def etherpad_html(self):
|
||||||
|
if not self.__get_pad_name():
|
||||||
|
return None
|
||||||
|
|
||||||
|
return get_pad_html(self.__get_pad_name())
|
||||||
|
|
||||||
|
|
||||||
class FileUpload(models.Model):
|
class FileUpload(models.Model):
|
||||||
|
|||||||
17
fet2020/intern/search_indexes.py
Normal file
17
fet2020/intern/search_indexes.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
from haystack import indexes
|
||||||
|
from html2text import html2text
|
||||||
|
|
||||||
|
from .models import Etherpad
|
||||||
|
|
||||||
|
|
||||||
|
class EtherpadIndex(indexes.SearchIndex, indexes.Indexable):
|
||||||
|
text = indexes.CharField(document=True, use_template=True)
|
||||||
|
title = indexes.CharField(model_attr="title")
|
||||||
|
date = indexes.DateField(model_attr="date")
|
||||||
|
etherpad = indexes.EdgeNgramField(null=True)
|
||||||
|
|
||||||
|
def get_model(self):
|
||||||
|
return Etherpad
|
||||||
|
|
||||||
|
def prepare_etherpad(self, obj):
|
||||||
|
return html2text(obj.etherpad_html)
|
||||||
38
fet2020/posts/search_indexes.py
Normal file
38
fet2020/posts/search_indexes.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
from haystack import indexes
|
||||||
|
from html2text import html2text
|
||||||
|
|
||||||
|
from .models import Post
|
||||||
|
|
||||||
|
|
||||||
|
class PostIndex(indexes.SearchIndex, indexes.Indexable):
|
||||||
|
text = indexes.CharField(document=True, use_template=True)
|
||||||
|
title = indexes.EdgeNgramField(model_attr="title")
|
||||||
|
body = indexes.EdgeNgramField(model_attr="body", null=True)
|
||||||
|
status = indexes.EdgeNgramField(model_attr="status")
|
||||||
|
date = indexes.DateField()
|
||||||
|
agenda = indexes.EdgeNgramField(null=True)
|
||||||
|
protocol = indexes.EdgeNgramField(null=True)
|
||||||
|
|
||||||
|
def get_model(self):
|
||||||
|
return Post
|
||||||
|
|
||||||
|
def index_queryset(self, using=None):
|
||||||
|
return self.get_model().objects.date_sorted_list(public=False)
|
||||||
|
|
||||||
|
def prepare_date(self, obj):
|
||||||
|
if obj.post_type == "N":
|
||||||
|
return obj.public_date
|
||||||
|
elif obj.post_type == "E":
|
||||||
|
return obj.event_start.date()
|
||||||
|
elif obj.post_type == "F":
|
||||||
|
return obj.event_start.date()
|
||||||
|
|
||||||
|
def prepare_agenda(self, obj):
|
||||||
|
if obj.has_agenda:
|
||||||
|
return html2text(obj.agenda_html)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def prepare_protocol(self, obj):
|
||||||
|
if obj.has_protocol:
|
||||||
|
return html2text(obj.protocol_html)
|
||||||
|
return None
|
||||||
@@ -3,6 +3,7 @@ django-ckeditor==6.2.0
|
|||||||
django-crontab==0.7.1
|
django-crontab==0.7.1
|
||||||
django-environ==0.8.1
|
django-environ==0.8.1
|
||||||
django-filter==21.1
|
django-filter==21.1
|
||||||
|
django-haystack==3.2.dev0
|
||||||
django-static-jquery-ui==1.12.1.1
|
django-static-jquery-ui==1.12.1.1
|
||||||
django-softhyphen==1.1.0
|
django-softhyphen==1.1.0
|
||||||
django-taggit==2.1.0
|
django-taggit==2.1.0
|
||||||
|
|||||||
0
fet2020/search/__init__.py
Normal file
0
fet2020/search/__init__.py
Normal file
60
fet2020/search/forms.py
Normal file
60
fet2020/search/forms.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
from collections import deque
|
||||||
|
|
||||||
|
from haystack.forms import SearchForm
|
||||||
|
from haystack.query import SQ
|
||||||
|
|
||||||
|
from intern.models import Etherpad
|
||||||
|
from posts.models import Post
|
||||||
|
|
||||||
|
|
||||||
|
class FetUserSearchForm(SearchForm):
|
||||||
|
def search(self):
|
||||||
|
if not self.is_valid():
|
||||||
|
return self.no_query_found()
|
||||||
|
|
||||||
|
if not self.cleaned_data.get("q"):
|
||||||
|
return self.no_query_found()
|
||||||
|
|
||||||
|
sqs_post = self.searchqueryset.models(Post)
|
||||||
|
sqs_post = sqs_post.filter(
|
||||||
|
SQ(title__icontains=self.cleaned_data["q"])
|
||||||
|
| SQ(body__icontains=self.cleaned_data["q"])
|
||||||
|
| SQ(agenda__icontains=self.cleaned_data["q"])
|
||||||
|
| SQ(protocol__icontains=self.cleaned_data["q"])
|
||||||
|
)
|
||||||
|
|
||||||
|
sqs_intern = self.searchqueryset.models(Etherpad)
|
||||||
|
sqs_intern = sqs_intern.filter(etherpad__icontains=self.cleaned_data["q"])
|
||||||
|
|
||||||
|
results = deque([])
|
||||||
|
|
||||||
|
for elem in sqs_post.order_by("-date"):
|
||||||
|
results.append(elem.object)
|
||||||
|
|
||||||
|
for elem in sqs_intern.order_by("-date"):
|
||||||
|
results.append(elem.object)
|
||||||
|
|
||||||
|
return sorted(results, key=lambda elem: elem.date, reverse=True)
|
||||||
|
|
||||||
|
|
||||||
|
class NonUserSearchForm(SearchForm):
|
||||||
|
def search(self):
|
||||||
|
if not self.is_valid():
|
||||||
|
return self.no_query_found()
|
||||||
|
|
||||||
|
if not self.cleaned_data.get("q"):
|
||||||
|
return self.no_query_found()
|
||||||
|
|
||||||
|
sqs_post = self.searchqueryset.models(Post).filter(status="20")
|
||||||
|
sqs_post = sqs_post.filter(
|
||||||
|
SQ(title__icontains=self.cleaned_data["q"])
|
||||||
|
| SQ(body__icontains=self.cleaned_data["q"])
|
||||||
|
| SQ(agenda__icontains=self.cleaned_data["q"])
|
||||||
|
)
|
||||||
|
|
||||||
|
results = deque([])
|
||||||
|
|
||||||
|
for elem in sqs_post.order_by("-date"):
|
||||||
|
results.append(elem.object)
|
||||||
|
|
||||||
|
return sorted(results, key=lambda elem: elem.date, reverse=True)
|
||||||
7
fet2020/search/urls.py
Normal file
7
fet2020/search/urls.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
from django.urls import path, re_path
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path("", views.index, name="index"),
|
||||||
|
]
|
||||||
26
fet2020/search/views.py
Normal file
26
fet2020/search/views.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
from haystack.generic_views import SearchView
|
||||||
|
from haystack.query import SearchQuerySet
|
||||||
|
|
||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
from .forms import FetUserSearchForm, NonUserSearchForm
|
||||||
|
|
||||||
|
|
||||||
|
class FetUserSearchView(LoginRequiredMixin, SearchView):
|
||||||
|
template_name = "search/index.html"
|
||||||
|
queryset = SearchQuerySet()
|
||||||
|
form_class = FetUserSearchForm
|
||||||
|
|
||||||
|
|
||||||
|
class NonUserSearchView(SearchView):
|
||||||
|
template_name = "search/index.html"
|
||||||
|
queryset = SearchQuerySet()
|
||||||
|
form_class = NonUserSearchForm
|
||||||
|
|
||||||
|
|
||||||
|
def index(request):
|
||||||
|
if request.user.is_authenticated:
|
||||||
|
return FetUserSearchView.as_view()(request)
|
||||||
|
|
||||||
|
return NonUserSearchView.as_view()(request)
|
||||||
33
fet2020/templates/search/index.html
Normal file
33
fet2020/templates/search/index.html
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<main class="container mx-auto w-full px-4 my-8 flex-1">
|
||||||
|
<h1 class="page-title">Suchen</h1>
|
||||||
|
<div class="sm:flex sm:flex-row-reverse justify-center text-gray-700 dark:text-gray-200">
|
||||||
|
|
||||||
|
<form method="get" action=".">
|
||||||
|
<table>
|
||||||
|
{{ form.as_table }}
|
||||||
|
<tr>
|
||||||
|
<td> </td>
|
||||||
|
<td>
|
||||||
|
<input type="submit" value="Search">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% if object_list %}
|
||||||
|
<h3>Results</h3>
|
||||||
|
|
||||||
|
{% for result in object_list %}
|
||||||
|
<p>
|
||||||
|
<a href="{{ result.get_absolute_url }}">{{ result.date|date }}: {{ result.title }}</a>
|
||||||
|
</p>
|
||||||
|
{% empty %}
|
||||||
|
<p>No results found.</p>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{{ object.title }}
|
||||||
|
{{ object.date }}
|
||||||
|
{{ object.etherpad|safe }}
|
||||||
4
fet2020/templates/search/indexes/posts/post_text.txt
Normal file
4
fet2020/templates/search/indexes/posts/post_text.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{{ object.title }}
|
||||||
|
{{ object.body|safe }}
|
||||||
|
{{ object.agenda|safe }}
|
||||||
|
{{ object.protocol|safe }}
|
||||||
Reference in New Issue
Block a user