7

adds variation add view and fixes tests

This commit is contained in:
Boris Besemer
2016-11-15 13:36:41 +01:00
parent 0746352ed6
commit ce59df3f0a
10 changed files with 178 additions and 30 deletions

View File

@ -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

View File

@ -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',
]
) )

View File

@ -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)

View File

@ -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')
] ]

View File

@ -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

View File

@ -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"""

View File

@ -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

View File

@ -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

View File

@ -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
View 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',
],
},
},
]
)