adds variation add view and fixes tests
This commit is contained in:
@ -1,9 +1,8 @@
|
|||||||
[tool:pytest]
|
[tool:pytest]
|
||||||
DJANGO_SETTINGS_MODULE = tests._sandbox.settings
|
DJANGO_SETTINGS_MODULE = tests._sandbox.settings
|
||||||
norecursedirs = .tox .git
|
norecursedirs = .tox .git
|
||||||
django_find_project = true
|
|
||||||
|
|
||||||
[flake8]
|
[flake8]
|
||||||
ignore=E731
|
ignore=E731
|
||||||
exclude=
|
exclude=
|
||||||
src/**/migrations/*.py
|
src/**/migrations/*.py
|
||||||
|
21
setup.py
21
setup.py
@ -1,5 +1,17 @@
|
|||||||
from setuptools import find_packages, setup
|
from setuptools import find_packages, setup
|
||||||
|
|
||||||
|
|
||||||
|
install_requires = [
|
||||||
|
'django-polymorphic==1.0.2',
|
||||||
|
'wagtail>=1.7',
|
||||||
|
]
|
||||||
|
|
||||||
|
tests_require = [
|
||||||
|
'pytest==3.0.4',
|
||||||
|
'pytest-django==3.0.0',
|
||||||
|
'pytest-sugar==0.7.1',
|
||||||
|
]
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='wagtail-personalisation',
|
name='wagtail-personalisation',
|
||||||
version='0.1.0',
|
version='0.1.0',
|
||||||
@ -7,6 +19,11 @@ setup(
|
|||||||
author='Lab Digital BV',
|
author='Lab Digital BV',
|
||||||
author_email='b.besemer@labdigital.nl',
|
author_email='b.besemer@labdigital.nl',
|
||||||
url='http://labdigital.nl',
|
url='http://labdigital.nl',
|
||||||
|
install_requires=install_requires,
|
||||||
|
tests_require=tests_require,
|
||||||
|
extras_require={
|
||||||
|
'test': tests_require,
|
||||||
|
},
|
||||||
packages=find_packages('src'),
|
packages=find_packages('src'),
|
||||||
package_dir={'': 'src'},
|
package_dir={'': 'src'},
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
@ -27,8 +44,4 @@ setup(
|
|||||||
'Framework :: Django :: 1.10',
|
'Framework :: Django :: 1.10',
|
||||||
'Topic :: Internet :: WWW/HTTP :: Site Management',
|
'Topic :: Internet :: WWW/HTTP :: Site Management',
|
||||||
],
|
],
|
||||||
install_requires=[
|
|
||||||
'django-polymorphic==1.0.2',
|
|
||||||
'wagtail>=1.7',
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
@ -8,19 +8,29 @@ class TimeRuleAdminInline(admin.TabularInline):
|
|||||||
model = models.TimeRule
|
model = models.TimeRule
|
||||||
extra = 0
|
extra = 0
|
||||||
|
|
||||||
|
|
||||||
class ReferralRuleAdminInline(admin.TabularInline):
|
class ReferralRuleAdminInline(admin.TabularInline):
|
||||||
"""Inline the Referral Rule into the administration interface for segments"""
|
"""
|
||||||
|
Inline the Referral Rule into the administration interface for segments
|
||||||
|
"""
|
||||||
model = models.ReferralRule
|
model = models.ReferralRule
|
||||||
extra = 0
|
extra = 0
|
||||||
|
|
||||||
|
|
||||||
class VisitCountRuleAdminInline(admin.TabularInline):
|
class VisitCountRuleAdminInline(admin.TabularInline):
|
||||||
"""Inline the Visit Count Rule into the administration interface for segments"""
|
"""
|
||||||
|
Inline the Visit Count Rule into the administration interface for segments
|
||||||
|
"""
|
||||||
model = models.VisitCountRule
|
model = models.VisitCountRule
|
||||||
extra = 0
|
extra = 0
|
||||||
|
|
||||||
|
|
||||||
class SegmentAdmin(admin.ModelAdmin):
|
class SegmentAdmin(admin.ModelAdmin):
|
||||||
"""Add the inlines to the Segment admin interface"""
|
"""Add the inlines to the Segment admin interface"""
|
||||||
inlines = (TimeRuleAdminInline, ReferralRuleAdminInline, VisitCountRuleAdminInline)
|
inlines = (
|
||||||
|
TimeRuleAdminInline, ReferralRuleAdminInline,
|
||||||
|
VisitCountRuleAdminInline
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(models.Segment, SegmentAdmin)
|
admin.site.register(models.Segment, SegmentAdmin)
|
||||||
|
@ -6,12 +6,19 @@ from personalisation import views
|
|||||||
|
|
||||||
app_name = 'segment'
|
app_name = 'segment'
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^segment/(\d+)/$', views.overview, name='overview'),
|
url(r'^segment/(\d+)/$', views.overview,
|
||||||
# url(r'^segment/create/$', views.CreateSegmentView.as_view(), name='create'),
|
name='overview'),
|
||||||
url(r'^segment/(?P<segment_id>[0-9]+)/enable/$', views.enable, name='enable'),
|
url(r'^segment/(?P<segment_id>[0-9]+)/enable/$', views.enable,
|
||||||
url(r'^segment/(?P<segment_id>[0-9]+)/disable/$', views.disable, name='disable'),
|
name='enable'),
|
||||||
|
url(r'^segment/(?P<segment_id>[0-9]+)/disable/$', views.disable,
|
||||||
|
name='disable'),
|
||||||
# TODO: These might no longer be needed when using a modal
|
# TODO: These might no longer be needed when using a modal
|
||||||
url(r'^segment/time-rule/$', views.time_rule_embed, name="time_rule_embed"),
|
url(r'^segment/time-rule/$', views.time_rule_embed,
|
||||||
url(r'^segment/referral-rule/$', views.referral_rule_embed, name="refferal_rule_embed"),
|
name="time_rule_embed"),
|
||||||
url(r'^segment/visit-count-rule/$', views.visit_c_rule_embed, name="visit_count_rule_embed"),
|
url(r'^segment/referral-rule/$', views.referral_rule_embed,
|
||||||
|
name="refferal_rule_embed"),
|
||||||
|
url(r'^segment/visit-count-rule/$', views.visit_count_rule_embed,
|
||||||
|
name="visit_count_rule_embed"),
|
||||||
|
url(r'^(?P<page_pk>\d+)/add/(?P<segment_name>[^/]+)/$',
|
||||||
|
views.AddVariation.as_view(), name='add')
|
||||||
]
|
]
|
||||||
|
@ -12,33 +12,42 @@ from personalisation.models import (
|
|||||||
class SegmentForm(forms.ModelForm):
|
class SegmentForm(forms.ModelForm):
|
||||||
"""Custom Segment form for the create view."""
|
"""Custom Segment form for the create view."""
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Why does this need a docstring? I do not know."""
|
|
||||||
model = Segment
|
model = Segment
|
||||||
fields = (
|
fields = (
|
||||||
'name',
|
'name',
|
||||||
'status',
|
'status',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TimeRuleForm(WagtailAdminModelForm):
|
class TimeRuleForm(WagtailAdminModelForm):
|
||||||
"""Create a form for the time rule model."""
|
"""Create a form for the time rule model."""
|
||||||
title = "Time"
|
title = "Time"
|
||||||
description = "Choose a time segment in which the user visits the site."
|
description = """
|
||||||
|
Choose a time segment in which the user visits the site.
|
||||||
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = TimeRule
|
model = TimeRule
|
||||||
fields = ['start_time', 'end_time']
|
fields = ['start_time', 'end_time']
|
||||||
|
|
||||||
|
|
||||||
class ReferralRuleForm(WagtailAdminModelForm):
|
class ReferralRuleForm(WagtailAdminModelForm):
|
||||||
"""Create a form for the referral rule model."""
|
"""Create a form for the referral rule model."""
|
||||||
title = "Referrer"
|
title = "Referrer"
|
||||||
description = "Define a referring page, domain or query the user has to come from."
|
description = """
|
||||||
|
Define a referring page, domain or query the user has to come from.
|
||||||
|
"""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ReferralRule
|
model = ReferralRule
|
||||||
fields = ['regex_string']
|
fields = ['regex_string']
|
||||||
|
|
||||||
|
|
||||||
class VisitCountRuleForm(WagtailAdminModelForm):
|
class VisitCountRuleForm(WagtailAdminModelForm):
|
||||||
"""Create a form for the visit count rule model."""
|
"""Create a form for the visit count rule model."""
|
||||||
title = "Visit count"
|
title = "Visit count"
|
||||||
description = "Choose the number of visits the user has to have made."
|
description = "Choose the number of visits the user has to have made."
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = VisitCountRule
|
model = VisitCountRule
|
||||||
fields = ['operator', 'count']
|
fields = ['operator', 'count']
|
||||||
@ -76,7 +85,6 @@ class PersonalisationForm(forms.Form):
|
|||||||
return Page.objects.filter(pk=self.site.root_page.pk)
|
return Page.objects.filter(pk=self.site.root_page.pk)
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
|
|
||||||
def _page_has_required(self, page):
|
def _page_has_required(self, page):
|
||||||
common_fields = set(PersonalisablePage._meta.fields)
|
common_fields = set(PersonalisablePage._meta.fields)
|
||||||
specific_fields = set(page.specific._meta.fields) - common_fields
|
specific_fields = set(page.specific._meta.fields) - common_fields
|
||||||
|
@ -6,7 +6,6 @@ from personalisation.models import AbstractBaseRule, Segment
|
|||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SegmentMiddleware(object):
|
class SegmentMiddleware(object):
|
||||||
"""Middleware for testing and putting a user in a segment"""
|
"""Middleware for testing and putting a user in a segment"""
|
||||||
|
|
||||||
|
@ -82,7 +82,6 @@ class AbstractBaseRule(PolymorphicModel):
|
|||||||
return "Segmentation rule"
|
return "Segmentation rule"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class TimeRule(AbstractBaseRule):
|
class TimeRule(AbstractBaseRule):
|
||||||
"""Time rule to segment users based on a start and end time"""
|
"""Time rule to segment users based on a start and end time"""
|
||||||
@ -195,6 +194,7 @@ class VisitCountRule(AbstractBaseRule):
|
|||||||
operator_display = self.get_operator_display()
|
operator_display = self.get_operator_display()
|
||||||
return '{} {}'.format(operator_display, self.count)
|
return '{} {}'.format(operator_display, self.count)
|
||||||
|
|
||||||
|
|
||||||
class AdminPersonalisablePageForm(WagtailAdminPageForm):
|
class AdminPersonalisablePageForm(WagtailAdminPageForm):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(AdminPersonalisablePageForm, self).__init__(*args, **kwargs)
|
super(AdminPersonalisablePageForm, self).__init__(*args, **kwargs)
|
||||||
@ -297,7 +297,8 @@ def get_edit_handler(cls):
|
|||||||
if cls.settings_panels:
|
if cls.settings_panels:
|
||||||
tabs.append(ObjectList(cls.settings_panels, heading=_("Settings"), classname='settings'))
|
tabs.append(ObjectList(cls.settings_panels, heading=_("Settings"), classname='settings'))
|
||||||
|
|
||||||
EditHandler = TabbedInterface(tabs, base_form_class=cls.base_form_class)
|
edit_handler = TabbedInterface(tabs, base_form_class=cls.base_form_class)
|
||||||
return EditHandler.bind_to_model(cls)
|
return edit_handler.bind_to_model(cls)
|
||||||
|
|
||||||
|
|
||||||
PersonalisablePage.get_edit_handler = get_edit_handler
|
PersonalisablePage.get_edit_handler = get_edit_handler
|
||||||
|
@ -2,21 +2,25 @@ from __future__ import absolute_import, unicode_literals
|
|||||||
|
|
||||||
from django.forms import ModelForm
|
from django.forms import ModelForm
|
||||||
from django.http import HttpResponse, HttpResponseRedirect
|
from django.http import HttpResponse, HttpResponseRedirect
|
||||||
from django.shortcuts import get_object_or_404, render
|
from django.shortcuts import get_object_or_404, render, redirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.views.generic.edit import FormView
|
||||||
from wagtail.contrib.modeladmin.views import CreateView
|
from wagtail.contrib.modeladmin.views import CreateView
|
||||||
|
from wagtail.wagtailadmin.edit_handlers import (
|
||||||
|
FieldPanel, ObjectList, PageChooserPanel, TabbedInterface)
|
||||||
|
|
||||||
from personalisation.forms import (
|
from personalisation.forms import (
|
||||||
ReferralRuleForm, SegmentForm, TimeRuleForm, VisitCountRuleForm)
|
PersonalisationForm, ReferralRuleForm, SegmentForm, TimeRuleForm,
|
||||||
from personalisation.models import (
|
VisitCountRuleForm)
|
||||||
ReferralRule, Segment, TimeRule, VisitCountRule)
|
from personalisation.models import PersonalisablePage, Segment
|
||||||
|
|
||||||
|
|
||||||
def overview(request):
|
def overview(request):
|
||||||
"""Display segments overview. Dummy function"""
|
"""Display segments overview. Dummy function"""
|
||||||
return render(request, 'wagtailadmin/segment.html')
|
return render(request, 'wagtailadmin/segment.html')
|
||||||
|
|
||||||
|
|
||||||
def enable(request, segment_id):
|
def enable(request, segment_id):
|
||||||
"""Enable the selected segment"""
|
"""Enable the selected segment"""
|
||||||
segment = get_object_or_404(Segment, pk=segment_id)
|
segment = get_object_or_404(Segment, pk=segment_id)
|
||||||
@ -25,6 +29,7 @@ def enable(request, segment_id):
|
|||||||
|
|
||||||
return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
|
return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
|
||||||
|
|
||||||
|
|
||||||
def disable(request, segment_id):
|
def disable(request, segment_id):
|
||||||
"""Disable the selected segment"""
|
"""Disable the selected segment"""
|
||||||
segment = get_object_or_404(Segment, pk=segment_id)
|
segment = get_object_or_404(Segment, pk=segment_id)
|
||||||
@ -33,29 +38,87 @@ def disable(request, segment_id):
|
|||||||
|
|
||||||
return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
|
return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
|
||||||
|
|
||||||
# TODO: Make these requestable from an existing page (the create page.)
|
|
||||||
|
# TODO: Make these requestable from an existing page (the create page)
|
||||||
# This code might become obsolete.
|
# This code might become obsolete.
|
||||||
|
|
||||||
|
|
||||||
def time_rule_embed(request):
|
def time_rule_embed(request):
|
||||||
"""Show the content of the time rule modal."""
|
"""Show the content of the time rule modal."""
|
||||||
return render(request, 'wagtailadmin/embeds/time_rule.html', {
|
return render(request, 'wagtailadmin/embeds/time_rule.html', {
|
||||||
'form': TimeRuleForm,
|
'form': TimeRuleForm,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
def referral_rule_embed(request):
|
def referral_rule_embed(request):
|
||||||
"""Show the content of the referral rule modal."""
|
"""Show the content of the referral rule modal."""
|
||||||
return render(request, 'wagtailadmin/embeds/referral_rule.html', {
|
return render(request, 'wagtailadmin/embeds/referral_rule.html', {
|
||||||
'form': ReferralRuleForm,
|
'form': ReferralRuleForm,
|
||||||
})
|
})
|
||||||
|
|
||||||
def visit_c_rule_embed(request):
|
|
||||||
|
def visit_count_rule_embed(request):
|
||||||
"""Show the content of the visit count rule modal."""
|
"""Show the content of the visit count rule modal."""
|
||||||
return render(request, 'wagtailadmin/embeds/visit_count_rule.html', {
|
return render(request, 'wagtailadmin/embeds/visit_count_rule.html', {
|
||||||
'form': VisitCountRuleForm,
|
'form': VisitCountRuleForm,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
class CreateSegmentView(CreateView):
|
class CreateSegmentView(CreateView):
|
||||||
page_title = _("Add segment")
|
page_title = _("Add segment")
|
||||||
form_class = SegmentForm
|
form_class = SegmentForm
|
||||||
template_name = 'modeladmin/personalisation/segment/create.html'
|
template_name = 'modeladmin/personalisation/segment/create.html'
|
||||||
header_icon = 'folder-open-1'
|
header_icon = 'folder-open-1'
|
||||||
|
|
||||||
|
|
||||||
|
class AddVariation(FormView):
|
||||||
|
form_class = PersonalisationForm
|
||||||
|
|
||||||
|
add_variation_panels = [
|
||||||
|
FieldPanel('copy_from_canonical'),
|
||||||
|
PageChooserPanel('parent_page'),
|
||||||
|
]
|
||||||
|
|
||||||
|
edit_handler = TabbedInterface([
|
||||||
|
ObjectList(add_variation_panels, heading='Variation',
|
||||||
|
base_form_class=PersonalisationForm)
|
||||||
|
])
|
||||||
|
|
||||||
|
def dispatch(self, request, page_pk, segment_name, *args, **kwargs):
|
||||||
|
self.page = get_object_or_404(PersonalisablePage, pk=page_pk)
|
||||||
|
self.segment = get_object_or_404(Segment, name=segment_name)
|
||||||
|
|
||||||
|
super(AddVariation, self).dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def get_form_kwargs(self):
|
||||||
|
form_kwargs = super(AddVariation, self).get_form_kwargs(*args, **kwargs)
|
||||||
|
form_kwargs.update({
|
||||||
|
'page': self.page,
|
||||||
|
'segment': self.segment,
|
||||||
|
})
|
||||||
|
return form_kwargs
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
parent = form.cleaned_data['parent_page']
|
||||||
|
copy_from_canonical = form.cleaned_data['copy_from_canonical']
|
||||||
|
|
||||||
|
new_page = self.page.create_variation(
|
||||||
|
self.segment, copy_fields=copy_from_canonical, parent=parent)
|
||||||
|
|
||||||
|
return redirect(
|
||||||
|
'wagtailadmin_pages:edit', new_page.id
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_context_data(self, *args, **kwargs):
|
||||||
|
context = super(AddVariation, self).get_context_data(*args, **kwargs)
|
||||||
|
edit_handler = self.edit_handler.bind_to_model(self.page)
|
||||||
|
|
||||||
|
context.update({
|
||||||
|
'page': self.page,
|
||||||
|
'segment': self.segment,
|
||||||
|
'content_type': self.page.content_type,
|
||||||
|
'parent_page': self.page.get_parent(),
|
||||||
|
'edit_handler': edit_handler(self.page, context['form']),
|
||||||
|
})
|
||||||
|
|
||||||
|
return context
|
||||||
|
@ -21,6 +21,7 @@ def register_admin_urls():
|
|||||||
namespace='personalisation')),
|
namespace='personalisation')),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class SegmentCreateView(CreateView):
|
class SegmentCreateView(CreateView):
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = {
|
context = {
|
||||||
|
47
tests/conftest.py
Normal file
47
tests/conftest.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
|
def pytest_configure():
|
||||||
|
settings.configure(
|
||||||
|
MIDDLEWARE_CLASSES=[
|
||||||
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'personalisation.middleware.SegmentMiddleware',
|
||||||
|
],
|
||||||
|
CACHES={
|
||||||
|
'default': {
|
||||||
|
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||||
|
'LOCATION': 'unique-snowflake',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DATABASE={
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
|
'NAME': 'db.sqlite',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
INSTALLED_APPS=[
|
||||||
|
'django.contrib.admin',
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'django.contrib.messages',
|
||||||
|
'django.contrib.staticfiles',
|
||||||
|
'wagtail.contrib.modeladmin',
|
||||||
|
'personalisation',
|
||||||
|
],
|
||||||
|
TEMPLATES=[
|
||||||
|
{
|
||||||
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
|
'DIRS': [],
|
||||||
|
'APP_DIRS': True,
|
||||||
|
'OPTIONS': {
|
||||||
|
'context_processors': [
|
||||||
|
'django.template.context_processors.debug',
|
||||||
|
'django.template.context_processors.request',
|
||||||
|
'django.contrib.auth.context_processors.auth',
|
||||||
|
'django.contrib.messages.context_processors.messages',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
)
|
Reference in New Issue
Block a user