161 lines
4.4 KiB
Python
161 lines
4.4 KiB
Python
import datetime
|
|
|
|
from django.conf import settings
|
|
from django.db import models
|
|
from django.utils import timezone
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from wagtail.search.utils import MAX_QUERY_STRING_LENGTH, normalise_query_string
|
|
|
|
|
|
class Query(models.Model):
|
|
query_string = models.CharField(max_length=MAX_QUERY_STRING_LENGTH, unique=True)
|
|
|
|
def save(self, *args, **kwargs):
|
|
# Normalise query string
|
|
self.query_string = normalise_query_string(self.query_string)
|
|
|
|
super().save(*args, **kwargs)
|
|
|
|
def add_hit(self, date=None):
|
|
if date is None:
|
|
date = timezone.now().date()
|
|
daily_hits, created = QueryDailyHits.objects.get_or_create(
|
|
query=self, date=date
|
|
)
|
|
daily_hits.hits = models.F("hits") + 1
|
|
daily_hits.save()
|
|
|
|
def __str__(self):
|
|
return self.query_string
|
|
|
|
@property
|
|
def hits(self):
|
|
hits = self.daily_hits.aggregate(models.Sum("hits"))["hits__sum"]
|
|
return hits if hits else 0
|
|
|
|
@classmethod
|
|
def garbage_collect(cls):
|
|
"""
|
|
Deletes all Query records that have no daily hits or editors picks
|
|
"""
|
|
extra_filter_kwargs = (
|
|
{
|
|
"editors_picks__isnull": True,
|
|
}
|
|
if hasattr(cls, "editors_picks")
|
|
else {}
|
|
)
|
|
cls.objects.filter(daily_hits__isnull=True, **extra_filter_kwargs).delete()
|
|
|
|
@classmethod
|
|
def get(cls, query_string):
|
|
return cls.objects.get_or_create(
|
|
query_string=normalise_query_string(query_string)
|
|
)[0]
|
|
|
|
@classmethod
|
|
def get_most_popular(cls, date_since=None):
|
|
objects = cls.objects.filter(daily_hits__isnull=False)
|
|
|
|
if date_since:
|
|
objects = objects.filter(daily_hits__date__gte=date_since)
|
|
|
|
return (
|
|
objects.annotate(_hits=models.Sum("daily_hits__hits"))
|
|
.distinct()
|
|
.order_by("-_hits")
|
|
)
|
|
|
|
|
|
class QueryDailyHits(models.Model):
|
|
query = models.ForeignKey(
|
|
Query, db_index=True, related_name="daily_hits", on_delete=models.CASCADE
|
|
)
|
|
date = models.DateField()
|
|
hits = models.IntegerField(default=0)
|
|
|
|
@classmethod
|
|
def garbage_collect(cls, days=None):
|
|
"""
|
|
Deletes all QueryDailyHits records that are older than a set number of days
|
|
"""
|
|
days = (
|
|
getattr(settings, "WAGTAILSEARCH_HITS_MAX_AGE", 7) if days is None else days
|
|
)
|
|
min_date = timezone.now().date() - datetime.timedelta(days)
|
|
|
|
cls.objects.filter(date__lt=min_date).delete()
|
|
|
|
class Meta:
|
|
unique_together = (("query", "date"),)
|
|
verbose_name = _("Query Daily Hits")
|
|
verbose_name_plural = _("Query Daily Hits")
|
|
|
|
|
|
class SearchPromotion(models.Model):
|
|
query = models.ForeignKey(
|
|
Query, db_index=True, related_name="editors_picks", on_delete=models.CASCADE
|
|
)
|
|
page = models.ForeignKey(
|
|
"wagtailcore.Page",
|
|
verbose_name=_("page"),
|
|
help_text=_("Choose an internal page for this promotion"),
|
|
on_delete=models.CASCADE,
|
|
null=True,
|
|
blank=True,
|
|
)
|
|
external_link_url = models.URLField(
|
|
_("External link URL"),
|
|
help_text=_("Alternatively, use an external link for this promotion"),
|
|
blank=True,
|
|
)
|
|
external_link_text = models.CharField(
|
|
max_length=200,
|
|
blank=True,
|
|
null=True,
|
|
)
|
|
description = models.TextField(
|
|
verbose_name=_("description"),
|
|
help_text=_("Applies to internal page or external link"),
|
|
blank=True,
|
|
)
|
|
sort_order = models.IntegerField(null=True, blank=True, editable=False)
|
|
|
|
@property
|
|
def title(self):
|
|
if self.page:
|
|
prop = self.page.title
|
|
else:
|
|
prop = self.external_link_text
|
|
return prop
|
|
|
|
@property
|
|
def link(self):
|
|
if self.page:
|
|
prop = self.page
|
|
else:
|
|
prop = self.external_link_url
|
|
return prop
|
|
|
|
def __repr__(self):
|
|
if self.page:
|
|
label = "page"
|
|
else:
|
|
label = "external link"
|
|
|
|
return (
|
|
'SearchPromotion(query="'
|
|
+ self.query.query_string
|
|
+ f'", {label}="'
|
|
+ self.title
|
|
+ '")'
|
|
)
|
|
|
|
def __str__(self):
|
|
return f"{self.query.query_string} - {self.title}"
|
|
|
|
class Meta:
|
|
ordering = ("sort_order",)
|
|
verbose_name = _("search promotion")
|