Add basic search for govplans

This commit is contained in:
Stefan Wehrmeyer 2022-03-14 13:42:08 +01:00
parent be6717c2a0
commit 2b6a1805dd
4 changed files with 85 additions and 2 deletions

View file

@ -1,5 +1,9 @@
import functools
import re
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector
from django.db import models from django.db import models
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
@ -84,6 +88,48 @@ class CategorizedGovernmentPlan(TaggedItemBase):
verbose_name_plural = _("Categorized Government Plans") verbose_name_plural = _("Categorized Government Plans")
WORD_RE = re.compile(r"^\w+$", re.IGNORECASE)
class GovernmentPlanManager(models.Manager):
SEARCH_LANG = "german"
def get_search_vector(self):
fields = [
("title", "A"),
("description", "B"),
("quote", "B"),
]
return functools.reduce(
lambda a, b: a + b,
[SearchVector(f, weight=w, config=self.SEARCH_LANG) for f, w in fields],
)
def search(self, query, qs=None):
if not qs:
qs = self.get_queryset()
if not query:
return qs
search_queries = []
for q in query.split():
if WORD_RE.match(q):
sq = SearchQuery(
"{}:*".format(q), search_type="raw", config=self.SEARCH_LANG
)
else:
sq = SearchQuery(q, search_type="plain", config=self.SEARCH_LANG)
search_queries.append(sq)
search_query = functools.reduce(lambda a, b: a & b, search_queries)
search_vector = self.get_search_vector()
qs = (
qs.annotate(rank=SearchRank(search_vector, search_query))
.filter(rank__gte=0.3)
.order_by("-rank")
)
return qs
class GovernmentPlan(models.Model): class GovernmentPlan(models.Model):
government = models.ForeignKey( government = models.ForeignKey(
Government, on_delete=models.CASCADE, verbose_name=_("government") Government, on_delete=models.CASCADE, verbose_name=_("government")
@ -142,6 +188,8 @@ class GovernmentPlan(models.Model):
Group, null=True, blank=True, on_delete=models.SET_NULL, verbose_name=_("group") Group, null=True, blank=True, on_delete=models.SET_NULL, verbose_name=_("group")
) )
objects = GovernmentPlanManager()
class Meta: class Meta:
ordering = ("reference", "title") ordering = ("reference", "title")
verbose_name = _("Government plan") verbose_name = _("Government plan")
@ -313,6 +361,7 @@ if CMSPlugin:
("froide_govplan/plugins/default.html", _("Normal")), ("froide_govplan/plugins/default.html", _("Normal")),
("froide_govplan/plugins/progress.html", _("Progress")), ("froide_govplan/plugins/progress.html", _("Progress")),
("froide_govplan/plugins/card_cols.html", _("Card columns")), ("froide_govplan/plugins/card_cols.html", _("Card columns")),
("froide_govplan/plugins/search.html", _("Search")),
] ]
class GovernmentPlansCMSPlugin(CMSPlugin): class GovernmentPlansCMSPlugin(CMSPlugin):

View file

@ -0,0 +1,17 @@
{% load i18n %}
<form method="get" action="{% url 'govplan:search' %}" class="ajaxified" data-container="#govplan-searchresult-{{ instance.id }}">
<div class="input-group mb-3">
<input type="text" name="q" class="form-control" aria-label="{% translate 'search query' %}">
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="submit">
{% translate "Search" %}
</button>
</div>
</div>
{% if instance.government_id %}
<input type="hidden" name="government" value="{{ instance.government_id }}"/>
{% endif %}
</form>
<div id="govplan-searchresult-{{ instance.id }}">
</div>

View file

@ -2,12 +2,13 @@ from django.urls import path
from django.utils.translation import pgettext_lazy from django.utils.translation import pgettext_lazy
from .admin import govplan_admin_site from .admin import govplan_admin_site
from .views import GovPlanDetailView, GovPlanSectionDetailView from .views import GovPlanDetailView, GovPlanSectionDetailView, search
app_name = "govplan" app_name = "govplan"
urlpatterns = [ urlpatterns = [
path("admin/", govplan_admin_site.urls), path("admin/", govplan_admin_site.urls),
path("search/", search, name="search"),
path( path(
pgettext_lazy("url part", "<slug:gov>/plan/<slug:plan>/"), pgettext_lazy("url part", "<slug:gov>/plan/<slug:plan>/"),
GovPlanDetailView.as_view(), GovPlanDetailView.as_view(),

View file

@ -1,4 +1,4 @@
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404, render
from django.views.generic import DetailView from django.views.generic import DetailView
from .models import Government, GovernmentPlan, GovernmentPlanSection from .models import Government, GovernmentPlan, GovernmentPlanSection
@ -53,3 +53,19 @@ class GovPlanDetailView(GovernmentMixin, DetailView):
"-timestamp" "-timestamp"
) )
return context return context
def search(request):
plans = GovernmentPlan.objects.search(request.GET.get("q", ""))
if request.GET.get("government"):
try:
gov_id = int(request.GET["government"])
plans = plans.filter(government_id=gov_id)
except ValueError:
pass
plans = plans[:20]
return render(
request, "froide_govplan/plugins/card_cols.html", {"object_list": plans}
)