Add basic search for govplans
This commit is contained in:
parent
be6717c2a0
commit
2b6a1805dd
4 changed files with 85 additions and 2 deletions
|
|
@ -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):
|
||||||
|
|
|
||||||
17
froide_govplan/templates/froide_govplan/plugins/search.html
Normal file
17
froide_govplan/templates/froide_govplan/plugins/search.html
Normal 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>
|
||||||
|
|
@ -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(),
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue