7
This repository has been archived on 2023-05-07. You can view files and clone it, but cannot push or open issues or pull requests.
Files
cavemanon-wagtail-personali…/src/wagtail_personalisation/models.py

172 lines
5.7 KiB
Python

from __future__ import absolute_import, unicode_literals
from django.db import models
from django.db.models.signals import pre_save
from django.template.defaultfilters import slugify
from django.utils import timezone
from django.utils.encoding import python_2_unicode_compatible
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
from modelcluster.models import ClusterableModel
from wagtail.utils.decorators import cached_classmethod
from wagtail.wagtailadmin.edit_handlers import (
FieldPanel, FieldRowPanel, InlinePanel, MultiFieldPanel, ObjectList,
PageChooserPanel, TabbedInterface)
from wagtail.wagtailcore.models import Page
from wagtail_personalisation.forms import AdminPersonalisablePageForm
from wagtail_personalisation.rules import AbstractBaseRule
@python_2_unicode_compatible
class Segment(ClusterableModel):
"""The segment model."""
name = models.CharField(max_length=255)
create_date = models.DateTimeField(auto_now_add=True)
edit_date = models.DateTimeField(auto_now=True)
enable_date = models.DateTimeField(null=True, editable=False)
disable_date = models.DateTimeField(null=True, editable=False)
visit_count = models.PositiveIntegerField(default=0, editable=False)
STATUS_CHOICES = (
('enabled', 'Enabled'),
('disabled', 'Disabled'),
)
status = models.CharField(max_length=20, choices=STATUS_CHOICES,
default="enabled")
persistent = models.BooleanField(
default=False, help_text=_("Should the segment persist between visits?"))
match_any = models.BooleanField(
default=False,
help_text=_("Should the segment match all the rules or just one of them?")
)
def __init__(self, *args, **kwargs):
Segment.panels = [
MultiFieldPanel([
FieldPanel('name', classname="title"),
FieldRowPanel([
FieldPanel('status'),
FieldPanel('persistent'),
]),
FieldPanel('match_any'),
], heading="Segment"),
MultiFieldPanel([
InlinePanel(
"{}_related".format(rule._meta.db_table),
label=rule.__str__,
) for rule in AbstractBaseRule.__subclasses__()
], heading="Rules"),
]
super(Segment, self).__init__(*args, **kwargs)
def __str__(self):
return self.name
def encoded_name(self):
"""Return a string with a slug for the segment."""
return slugify(self.name.lower())
def get_rules(self):
"""Retrieve all rules in the segment."""
rules = AbstractBaseRule.__subclasses__()
segment_rules = []
for rule in rules:
segment_rules += rule.objects.filter(segment=self)
return segment_rules
def check_status_change(sender, instance, *args, **kwargs):
"""Check if the status has changed. Alter dates accordingly."""
try:
original_status = sender.objects.get(pk=instance.id).status
except sender.DoesNotExist:
original_status = ""
if original_status != instance.status:
if instance.status == "enabled":
instance.enable_date = timezone.now()
instance.visit_count = 0
return instance
if instance.status == "disabled":
instance.disable_date = timezone.now()
pre_save.connect(check_status_change, sender=Segment)
class PersonalisablePage(Page):
"""The personalisable page model. Allows creation of variants with linked
segments.
"""
canonical_page = models.ForeignKey(
'self', related_name='variations', on_delete=models.SET_NULL,
blank=True, null=True
)
segment = models.ForeignKey(
Segment, related_name='segments', on_delete=models.PROTECT,
blank=True, null=True
)
is_segmented = models.BooleanField(default=False)
variation_panels = [
MultiFieldPanel([
FieldPanel('segment'),
PageChooserPanel('canonical_page', page_type=None),
])
]
base_form_class = AdminPersonalisablePageForm
def __str__(self):
return "{}".format(self.title)
@cached_property
def has_variations(self):
"""Return a boolean indicating whether or not the personalisable page
has variations.
:returns: A boolean indicating whether or not the personalisable page
has variations.
:rtype: bool
"""
return self.variations.exists()
@cached_property
def is_canonical(self):
"""Return a boolean indicating whether or not the personalisable page
is a canonical page.
:returns: A boolean indicating whether or not the personalisable page
is a canonical page.
:rtype: bool
"""
return not self.canonical_page and self.has_variations
@cached_classmethod
def get_edit_handler(cls):
"""Add additional edit handlers to pages that are allowed to have
variations.
"""
tabs = []
if cls.content_panels:
tabs.append(ObjectList(cls.content_panels, heading=_("Content")))
if cls.variation_panels:
tabs.append(ObjectList(cls.variation_panels, heading=_("Variations")))
if cls.promote_panels:
tabs.append(ObjectList(cls.promote_panels, heading=_("Promote")))
if cls.settings_panels:
tabs.append(ObjectList(cls.settings_panels, heading=_("Settings"),
classname='settings'))
edit_handler = TabbedInterface(tabs, base_form_class=cls.base_form_class)
return edit_handler.bind_to_model(cls)
PersonalisablePage.get_edit_handler = get_edit_handler