8

Compare commits

..

2 Commits

Author SHA1 Message Date
Jasper Berghoef
4bbac1d542 Fix flake8 error in adapters.py 2018-10-30 10:11:19 +01:00
Jasper Berghoef
d9ef74c8f2 Prevent overriding media files as to load the page chooser
Fixes #194
2018-10-30 10:04:40 +01:00
39 changed files with 1258 additions and 3906 deletions

1
.gitignore vendored
View File

@@ -13,7 +13,6 @@
.vscode/
build/
ve/
dist/
htmlcov/
docs/_build

View File

@@ -14,28 +14,6 @@ matrix:
env: TOXENV=py36-django20-wagtail21
- python: 3.6
env: TOXENV=py36-django20-wagtail21-geoip2
- python: 3.6
env: TOXENV=py36-django20-wagtail22
- python: 3.6
env: TOXENV=py36-django20-wagtail22-geoip2
- python: 3.6
env: TOXENV=py36-django21-wagtail23
- python: 3.6
env: TOXENV=py36-django21-wagtail23-geoip2
- python: 3.6
env: TOXENV=py36-django21-wagtail24
- python: 3.6
env: TOXENV=py36-django21-wagtail24-geoip2
- python: 3.6
env: TOXENV=py36-django22-wagtail25
- python: 3.6
env: TOXENV=py36-django22-wagtail25-geoip2
- python: 3.6
env: TOXENV=py36-django22-wagtail26
- python: 3.6
env: TOXENV=py36-django22-wagtail26-geoip2
- python: 3.6
env: TOXENV=py36-django111-wagtail22
install:
- pip install tox codecov

View File

@@ -1,11 +1,3 @@
0.13.0
=================
- Merged Praekelt fork
- Add custom javascript to segment forms
- bugfix:exclude variant returns queryset when params is queryset
- Added RulePanel, a subclass of InlinePanel, for Rules
- Upgrade to Wagtail > 2.0, drop support for Wagtail < 2
0.12.0
==================
- Fix Django version classifier in setup.py

View File

@@ -54,7 +54,7 @@ master_doc = 'index'
# General information about the project.
project = 'wagtail-personalisation'
copyright = '2019, Lab Digital BV'
copyright = '2018, Lab Digital BV'
author = 'Lab Digital BV'
# The version info for the project you're documenting, acts as replacement for
@@ -62,10 +62,10 @@ author = 'Lab Digital BV'
# built documents.
#
# The short X.Y version.
version = '0.14.0'
version = '0.12.1'
# The full version, including alpha/beta/rc tags.
release = '0.14.0'
release = '0.12.1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View File

@@ -1,4 +1,4 @@
Django>=2.2,<2.3
wagtail>=2.6,<2.7
django-debug-toolbar==2.0
Django>=2.0,<2.1
wagtail>=2.1,<2.2
django-debug-toolbar==1.9.1
-e .[docs,test]

View File

@@ -1,20 +0,0 @@
# Generated by Django 2.1.7 on 2019-03-15 12:54
from django.db import migrations
import sandbox.apps.user.models
class Migration(migrations.Migration):
dependencies = [
('user', '0001_initial'),
]
operations = [
migrations.AlterModelManagers(
name='user',
managers=[
('objects', sandbox.apps.user.models.UserManager()),
],
),
]

View File

@@ -1,44 +1,14 @@
from django.contrib.auth.models import (
AbstractBaseUser, PermissionsMixin, BaseUserManager)
AbstractBaseUser, PermissionsMixin, UserManager)
from django.core.mail import send_mail
from django.db import models
from django.db import connections, models
from django.dispatch import receiver
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
class UserManager(BaseUserManager):
use_in_migrations = True
def _create_user(self, email, password, **extra_fields):
"""
Create and save a user with the given username, email, and password.
"""
if not email:
raise ValueError('The given email address must be set')
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_user(self, email, password=None, **extra_fields):
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_superuser', False)
return self._create_user(email, password, **extra_fields)
def create_superuser(self, email, password, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
if extra_fields.get('is_staff') is not True:
raise ValueError('Superuser must have is_staff=True.')
if extra_fields.get('is_superuser') is not True:
raise ValueError('Superuser must have is_superuser=True.')
return self._create_user(email, password, **extra_fields)
class User(AbstractBaseUser, PermissionsMixin):
"""Customized version of the default `AbstractUser` from Django.
"""Cusomtized version of the default `AbstractUser` from Django.
"""
first_name = models.CharField(_('first name'), max_length=100, blank=True)

View File

@@ -11,9 +11,9 @@ from wagtail.documents import urls as wagtaildocs_urls
from sandbox.apps.search import views as search_views
urlpatterns = [
url(r'^django-admin/', admin.site.urls),
url(r'^admin/', admin.site.urls),
url(r'^admin/', include(wagtailadmin_urls)),
url(r'^cms/', include(wagtailadmin_urls)),
url(r'^documents/', include(wagtaildocs_urls)),
url(r'^search/$', search_views.search, name='search'),

View File

@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.14.0
current_version = 0.12.1
commit = true
tag = true
tag_name = {new_version}

View File

@@ -20,7 +20,7 @@ tests_require = [
'pytest-pythonpath==0.7.2',
'pytest-sugar==0.9.1',
'pytest==3.4.2',
'wagtail_factories==1.1.0',
'wagtail_factories==1.0.0',
'pytest-mock==1.6.3',
]
@@ -35,7 +35,7 @@ with open('README.rst') as fh:
setup(
name='wagtail-personalisation',
version='0.14.0',
version='0.12.1',
description='A Wagtail add-on for showing personalized content',
author='Lab Digital BV and others',
author_email='opensource@labdigital.nl',
@@ -52,7 +52,7 @@ setup(
license='MIT',
long_description=long_description,
classifiers=[
'Development Status :: 5 - Production/Stable',
'Development Status :: 2 - Pre-Alpha',
'Environment :: Web Environment',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',

View File

@@ -1,3 +1,5 @@
from __future__ import absolute_import, unicode_literals
from django.conf import settings
from django.db.models import F
from django.utils.module_loading import import_string
@@ -194,10 +196,7 @@ class SessionSegmentsAdapter(BaseSegmentsAdapter):
for segment in enabled_segments:
if segment.is_static and segment.static_users.filter(id=self.request.user.id).exists():
additional_segments.append(segment)
elif any((
segment.excluded_users.filter(id=self.request.user.id).exists(),
segment in excluded_segments
)):
elif (segment.excluded_users.filter(id=self.request.user.id).exists() or segment in excluded_segments):
continue
elif not segment.is_static or not segment.is_full:
segment_rules = []

View File

@@ -1,3 +1,5 @@
from __future__ import absolute_import, unicode_literals
from django.contrib import admin
from wagtail_personalisation import models, rules

View File

@@ -1,3 +1,5 @@
from __future__ import absolute_import, unicode_literals
from django.conf.urls import url
from wagtail_personalisation import views

View File

@@ -1,3 +1,5 @@
from __future__ import absolute_import, unicode_literals
from django.utils.translation import ugettext_lazy as _
from wagtail.core import blocks

View File

@@ -1,3 +1,5 @@
from __future__ import absolute_import, unicode_literals
from datetime import datetime
from importlib import import_module

View File

@@ -1,7 +1,7 @@
# Generated by Django 2.0.7 on 2018-07-04 15:26
import django.db.models.deletion
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):

View File

@@ -1,7 +1,7 @@
# Generated by Django 2.0.5 on 2018-07-19 09:57
import django.db.models.deletion
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):

View File

@@ -1,8 +1,8 @@
# Generated by Django 2.0.6 on 2018-08-10 14:39
from django.db import migrations, models
import django.db.models.deletion
import modelcluster.fields
from django.db import migrations, models
class Migration(migrations.Migration):

File diff suppressed because one or more lines are too long

View File

@@ -1,15 +1,16 @@
import random
import wagtail
from django import forms
from django.conf import settings
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models, transaction
from django.template.defaultfilters import slugify
from django.utils.encoding import python_2_unicode_compatible
from django.utils.functional import cached_property
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from modelcluster.models import ClusterableModel
import wagtail
from wagtail.admin.edit_handlers import (
FieldPanel, FieldRowPanel, InlinePanel, MultiFieldPanel)
from wagtail.core.models import Page
@@ -20,19 +21,12 @@ from wagtail_personalisation.utils import count_active_days
from .forms import SegmentAdminForm
class RulePanel(InlinePanel):
def on_model_bound(self):
self.relation_name = self.relation_name.replace('_related', 's')
self.db_field = self.model._meta.get_field(self.relation_name)
manager = getattr(self.model, self.relation_name)
self.related = manager.rel
class SegmentQuerySet(models.QuerySet):
def enabled(self):
return self.filter(status=self.model.STATUS_ENABLED)
@python_2_unicode_compatible
class Segment(ClusterableModel):
"""The segment model."""
STATUS_ENABLED = 'enabled'
@@ -127,8 +121,8 @@ class Segment(ClusterableModel):
FieldPanel('randomisation_percent', classname='percent_field'),
], heading="Segment"),
MultiFieldPanel([
RulePanel(
"{}_related".format(rule_model._meta.db_table),
InlinePanel(
"{}s".format(rule_model._meta.db_table),
label='{}{}'.format(
rule_model._meta.verbose_name,
' ({})'.format(_('Static compatible')) if rule_model.static else ''

View File

@@ -1,5 +1,8 @@
from __future__ import absolute_import, unicode_literals
import logging
import re
from datetime import datetime
from importlib import import_module
import pycountry
@@ -10,7 +13,7 @@ from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.template.defaultfilters import slugify
from django.test.client import RequestFactory
from django.utils import timezone
from django.utils.encoding import force_text, python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
from modelcluster.fields import ParentalKey
from user_agents import parse
@@ -40,6 +43,7 @@ def get_geoip_module():
'warning if you use one of those.')
@python_2_unicode_compatible
class AbstractBaseRule(models.Model):
"""Base for creating rules to segment users with."""
icon = 'fa-circle-o'
@@ -55,7 +59,7 @@ class AbstractBaseRule(models.Model):
verbose_name = 'Abstract segmentation rule'
def __str__(self):
return str(self._meta.verbose_name)
return force_text(self._meta.verbose_name)
def test_user(self):
"""Test if the user matches this rule."""
@@ -63,7 +67,7 @@ class AbstractBaseRule(models.Model):
def encoded_name(self):
"""Return a string with a slug for the rule."""
return slugify(str(self).lower())
return slugify(force_text(self).lower())
def description(self):
"""Return a description explaining the functionality of the rule.
@@ -109,7 +113,7 @@ class TimeRule(AbstractBaseRule):
verbose_name = _('Time Rule')
def test_user(self, request=None):
return self.start_time <= timezone.now().time() <= self.end_time
return self.start_time <= datetime.now().time() <= self.end_time
def description(self):
return {
@@ -153,7 +157,7 @@ class DayRule(AbstractBaseRule):
def test_user(self, request=None):
return [self.mon, self.tue, self.wed, self.thu,
self.fri, self.sat, self.sun][timezone.now().date().weekday()]
self.fri, self.sat, self.sun][datetime.today().weekday()]
def description(self):
days = (

View File

@@ -37,6 +37,8 @@
/******/ 3: 0
/******/ };
/******/
/******/ var resolvedPromise = new Promise(function(resolve) { resolve(); });
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
@@ -64,21 +66,20 @@
/******/ // This file contains only the entry chunk.
/******/ // The chunk loading function for additional chunks
/******/ __webpack_require__.e = function requireEnsure(chunkId) {
/******/ var installedChunkData = installedChunks[chunkId];
/******/ if(installedChunkData === 0) {
/******/ return new Promise(function(resolve) { resolve(); });
/******/ if(installedChunks[chunkId] === 0) {
/******/ return resolvedPromise;
/******/ }
/******/
/******/ // a Promise means "currently loading".
/******/ if(installedChunkData) {
/******/ return installedChunkData[2];
/******/ if(installedChunks[chunkId]) {
/******/ return installedChunks[chunkId][2];
/******/ }
/******/
/******/ // setup Promise in chunk cache
/******/ var promise = new Promise(function(resolve, reject) {
/******/ installedChunkData = installedChunks[chunkId] = [resolve, reject];
/******/ installedChunks[chunkId] = [resolve, reject];
/******/ });
/******/ installedChunkData[2] = promise;
/******/ installedChunks[chunkId][2] = promise;
/******/
/******/ // start chunk loading
/******/ var head = document.getElementsByTagName('head')[0];

File diff suppressed because one or more lines are too long

View File

@@ -1,17 +1,6 @@
{% extends "modeladmin/index.html" %}
{% load i18n l10n staticfiles modeladmin_tags %}
{% block titletag %}{{ view.get_meta_title }}{% endblock %}
{% block css %}
{{ block.super }}
{{ view.media.css }}
{% endblock %}
{% block extra_js %}
{{ view.media.js }}
{% endblock %}
{% block header %}
<header class="nice-padding hasform">
<div class="row">

View File

@@ -1,5 +1,5 @@
{% extends "modeladmin/wagtail_personalisation/segment/base.html" %}
{% load i18n l10n staticfiles modeladmin_tags wagtail_personalisation_filters %}
{% load i18n l10n modeladmin_tags wagtail_personalisation_filters %}
{% block toggle_view %}to List {% endblock%}

View File

@@ -1,5 +1,3 @@
{% extends "modeladmin/wagtail_personalisation/segment/base.html" %}
{% load i18n l10n staticfiles modeladmin_tags wagtail_personalisation_filters %}
{% block toggle_view %}to Dashboard {% endblock%}

View File

@@ -1,3 +1,5 @@
from __future__ import absolute_import, unicode_literals
import csv
from django import forms
@@ -85,10 +87,7 @@ class SegmentModelAdmin(ModelAdmin):
'page_count', 'variant_count', 'statistics')
index_view_extra_js = ['js/commons.js', 'js/index.js']
index_view_extra_css = ['css/index.css']
form_view_extra_js = ['js/commons.js', 'js/form.js',
'js/segment_form_control.js',
'wagtailadmin/js/page-chooser-modal.js',
'wagtailadmin/js/page-chooser.js']
form_view_extra_js = ['js/commons.js', 'js/form.js']
form_view_extra_css = ['css/form.css']
def index_view(self, request):

View File

@@ -1,8 +1,9 @@
from __future__ import absolute_import, unicode_literals
import logging
from django.conf.urls import include, url
from django.db import transaction
from django.db.models import F
from django.http import Http404
from django.shortcuts import redirect, render
from django.template.defaultfilters import pluralize
@@ -18,7 +19,6 @@ from wagtail.core.models import Page
from wagtail_personalisation import admin_urls, models, utils
from wagtail_personalisation.adapters import get_segment_adapter
from wagtail_personalisation.models import PersonalisablePageMetadata
logger = logging.getLogger(__name__)
@@ -227,7 +227,8 @@ class PersonalisedPagesSummaryPanel(PagesSummaryItem):
order = 2100
def render(self):
page_count = models.PersonalisablePageMetadata.objects.filter(segment__isnull=True).count()
page_count = models.PersonalisablePageMetadata.objects.filter(
segment__isnull=True).count()
title = _("Personalised Page")
return mark_safe("""
<li class="icon icon-fa-file-o">
@@ -275,22 +276,14 @@ def delete_related_variants(request, page):
if request.method == 'POST':
parent_id = page.get_parent().id
variants_metadata = variants_metadata.select_related('variant')
with transaction.atomic():
# To ensure variants are deleted for all descendants, start with
# the deepest ones, and explicitly delete variants and metadata
# for all of them, including the page itself. Otherwise protected
# foreign key constraints are violated. Only consider canonical
# pages.
for metadata in PersonalisablePageMetadata.objects.filter(
canonical_page__in=page.get_descendants(inclusive=True),
variant=F("canonical_page"),
).order_by('-canonical_page__depth'):
for variant_metadata in metadata.variants_metadata.select_related('variant'):
# Call delete() on objects to trigger any signals or hooks.
variant_metadata.variant.delete()
metadata.delete()
metadata.canonical_page.delete()
for metadata in variants_metadata.iterator():
# Call delete() on objects to trigger any signals or hooks.
metadata.variant.delete()
# Delete the page's main variant and the page itself.
page.personalisation_metadata.delete()
page.delete()
msg = _("Page '{0}' and its variants deleted.")
messages.success(
request,

View File

@@ -1,3 +1,5 @@
from __future__ import absolute_import, unicode_literals
import pytest
pytest_plugins = [
@@ -5,11 +7,6 @@ pytest_plugins = [
]
@pytest.fixture(autouse=True)
def enable_db_access(db):
pass
@pytest.fixture(scope='session')
def django_db_setup(django_db_setup, django_db_blocker):
from wagtail.core.models import Page, Site

View File

@@ -5,7 +5,6 @@ from django.utils.text import slugify
from wagtail_factories.factories import PageFactory
from tests.site.pages import models
from wagtail_personalisation.models import PersonalisablePageMetadata
class ContentPageFactory(PageFactory):
@@ -23,9 +22,3 @@ class RegularPageFactory(PageFactory):
class Meta:
model = models.RegularPage
class PersonalisablePageMetadataFactory(factory.DjangoModelFactory):
class Meta:
model = PersonalisablePageMetadata

View File

@@ -1,3 +1,5 @@
from __future__ import absolute_import, unicode_literals
import os
DATABASES = {
@@ -50,7 +52,6 @@ TEMPLATES = [
},
]
MIDDLEWARE = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',

View File

@@ -1,3 +1,5 @@
from __future__ import absolute_import, unicode_literals
import datetime
import pytest
@@ -6,7 +8,7 @@ from django.db.models import ProtectedError
from tests.factories.page import ContentPageFactory
from tests.factories.segment import SegmentFactory
from tests.site.pages import models
from wagtail_personalisation.models import PersonalisablePageMetadata, Segment
from wagtail_personalisation.models import PersonalisablePageMetadata
from wagtail_personalisation.rules import TimeRule
@@ -71,10 +73,3 @@ def test_sitemap_generation_for_canonical_pages_is_enabled(segmented_page):
def test_sitemap_generation_for_variants_is_disabled(segmented_page):
assert not segmented_page.personalisation_metadata.is_canonical
assert not segmented_page.get_sitemap_urls()
@pytest.mark.django_db
def test_segment_edit_view(site, client, django_user_model):
test_segment = SegmentFactory()
new_panel = test_segment.panels[1].children[0].bind_to_model(Segment)
assert new_panel.related.name == "wagtail_personalisation_timerules"

View File

@@ -1,5 +1,5 @@
from importlib.util import find_spec
from unittest.mock import MagicMock, call, patch
from unittest.mock import call, MagicMock, patch
import pytest
@@ -7,6 +7,7 @@ from tests.factories.rule import OriginCountryRuleFactory
from tests.factories.segment import SegmentFactory
from wagtail_personalisation.rules import get_geoip_module
skip_if_geoip2_installed = pytest.mark.skipif(
find_spec('geoip2'), reason='requires GeoIP2 to be not installed'
)

View File

@@ -487,7 +487,7 @@ def test_count_users_matching_static_rules(site, client, mocker, django_user_mod
form = form_with_data(segment, rule)
mocker.patch('wagtail_personalisation.rules.VisitCountRule.test_user', return_value=True)
assert form.count_matching_users([rule], True) == 2
assert form.count_matching_users([rule], True) is 2
@pytest.mark.django_db
@@ -500,7 +500,7 @@ def test_count_matching_users_excludes_staff(site, client, mocker, django_user_m
form = form_with_data(segment, rule)
mock_test_user = mocker.patch('wagtail_personalisation.rules.VisitCountRule.test_user', return_value=True)
assert form.count_matching_users([rule], True) == 1
assert form.count_matching_users([rule], True) is 1
assert mock_test_user.call_count == 1
@@ -514,7 +514,7 @@ def test_count_matching_users_excludes_inactive(site, client, mocker, django_use
form = form_with_data(segment, rule)
mock_test_user = mocker.patch('wagtail_personalisation.rules.VisitCountRule.test_user', return_value=True)
assert form.count_matching_users([rule], True) == 1
assert form.count_matching_users([rule], True) is 1
assert mock_test_user.call_count == 1
@@ -532,7 +532,7 @@ def test_count_matching_users_only_counts_static_rules(site, client, mocker, dja
form = form_with_data(segment, rule)
mock_test_user = mocker.patch('wagtail_personalisation.rules.TimeRule.test_user')
assert form.count_matching_users([rule], True) == 0
assert form.count_matching_users([rule], True) is 0
assert mock_test_user.call_count == 0
@@ -551,7 +551,7 @@ def test_count_matching_users_handles_match_any(site, client, mocker, django_use
'wagtail_personalisation.rules.VisitCountRule.test_user',
side_effect=[True, False, True, False])
assert form.count_matching_users([first_rule, second_rule], True) == 2
assert form.count_matching_users([first_rule, second_rule], True) is 2
mock_test_user.call_count == 4
@@ -570,5 +570,5 @@ def test_count_matching_users_handles_match_all(site, client, mocker, django_use
'wagtail_personalisation.rules.VisitCountRule.test_user',
side_effect=[True, True, False, True])
assert form.count_matching_users([first_rule, second_rule], False) == 1
assert form.count_matching_users([first_rule, second_rule], False) is 1
mock_test_user.call_count == 4

View File

@@ -1,11 +1,10 @@
import pytest
from django.test import override_settings
from wagtail.core.models import Page as WagtailPage
from tests.factories.page import (
ContentPageFactory, PersonalisablePageMetadataFactory)
from django.test import override_settings
from tests.factories.page import ContentPageFactory
from wagtail_personalisation.utils import (
can_delete_pages, exclude_variants, get_client_ip, impersonate_other_page)
can_delete_pages, get_client_ip, impersonate_other_page)
@pytest.fixture
@@ -65,57 +64,3 @@ def test_get_client_ip_custom_get_client_ip_function_does_not_exist(rf):
)
def test_get_client_ip_custom_get_client_ip_used(rf):
assert get_client_ip(rf.get('/')) == '123.123.123.123'
def test_exclude_variants_with_pages_querysets():
'''
Test that excludes variant works for querysets
'''
for i in range(5):
page = ContentPageFactory(path="/" + str(i), depth=0, url_path="/", title="Hoi " + str(i))
page.save()
pages = WagtailPage.objects.all().specific().order_by('id')
result = exclude_variants(pages)
assert type(result) == type(pages)
assert set(result.values_list('pk', flat=True)) == set(pages.values_list('pk', flat=True))
def test_exclude_variants_with_pages_querysets_not_canonical():
'''
Test that excludes variant works for querysets with
personalisation_metadata canonical False
'''
for i in range(5):
page = ContentPageFactory(path="/" + str(i), depth=0, url_path="/", title="Hoi " + str(i))
page.save()
pages = WagtailPage.objects.all().specific().order_by('id')
# add variants
for page in pages:
variant = ContentPageFactory(title='variant %d' % page.pk)
page.personalisation_metadata = PersonalisablePageMetadataFactory(canonical_page=page, variant=variant)
page.save()
pages = WagtailPage.objects.all().specific()
result = exclude_variants(pages)
assert type(result) == type(pages)
assert result.count() < pages.count()
def test_exclude_variants_with_pages_querysets_meta_none():
'''
Test that excludes variant works for querysets with meta as none
'''
for i in range(5):
page = ContentPageFactory(path="/" + str(i), depth=0, url_path="/", title="Hoi " + str(i))
page.save()
pages = WagtailPage.objects.all().specific().order_by('id')
# add variants
for page in pages:
page.personalisation_metadata = PersonalisablePageMetadataFactory(canonical_page=page, variant=page)
page.save()
pages = WagtailPage.objects.all().specific()
result = exclude_variants(pages)
assert type(result) == type(pages)
assert set(result.values_list('pk', flat=True)) == set(pages.values_list('pk', flat=True))

View File

@@ -6,7 +6,7 @@ from wagtail.core.models import Page
from wagtail_personalisation.models import Segment
from wagtail_personalisation.rules import VisitCountRule
from wagtail_personalisation.views import (
SegmentModelAdmin, SegmentModelDeleteView)
SegmentModelDeleteView, SegmentModelAdmin)
@pytest.mark.django_db

View File

@@ -1,8 +1,9 @@
import pytest
from django.http import Http404
from wagtail.core.models import Page
from tests.factories.page import ContentPageFactory
from tests.factories.segment import SegmentFactory
from wagtail_personalisation import adapters, wagtail_hooks
@@ -123,21 +124,3 @@ def test_custom_delete_page_view_deletes_variants(rf, segmented_page, user):
# Make sure all the variant pages have been deleted.
assert not len(variants.all())
assert not len(variants_metadata.all())
@pytest.mark.django_db
def test_custom_delete_page_view_deletes_variants_of_child_pages(rf, segmented_page, user):
"""
Regression test for deleting pages that have children with variants
"""
post_request = rf.post('/')
user.is_superuser = True
rf.user = user
canonical_page = segmented_page.personalisation_metadata.canonical_page
# Create a child with a variant
child_page = ContentPageFactory(parent=canonical_page, slug='personalised-child')
child_page.personalisation_metadata.copy_for_segment(segmented_page.personalisation_metadata.segment)
# A ProtectedError would be raised if the bug persists
wagtail_hooks.delete_related_variants(
post_request, canonical_page
)

12
tox.ini
View File

@@ -1,5 +1,5 @@
[tox]
envlist = py{36}-django{111,20,21,22}-wagtail{20,21,22,23,24,25,26}{,-geoip2},lint
envlist = py{36}-django{20}-wagtail{20,21}{,-geoip2},lint
[testenv]
basepython = python3.6
@@ -7,17 +7,9 @@ commands = coverage run --parallel -m pytest -rs {posargs}
extras = test
deps =
django20: django>=2.0,<2.1
django21: django>=2.1,<2.2
django22: django>=2.2,<2.3
wagtail20: wagtail>=2.0,<2.1
wagtail21: wagtail>=2.1,<2.2
wagtail22: wagtail>=2.2,<2.3
wagtail23: wagtail>=2.3,<2.4
wagtail24: wagtail>=2.4,<2.5
wagtail25: wagtail>=2.5,<2.6
wagtail26: wagtail>=2.6,<2.7
geoip2: geoip2
django111: django>=1.11,<1.12
[testenv:coverage-report]
basepython = python3.6
@@ -29,7 +21,7 @@ commands =
[testenv:lint]
basepython = python3.6
deps = flake8==3.5.0
deps = flake8
commands =
flake8 src tests setup.py
isort -q --recursive --diff src/ tests/

4773
yarn.lock

File diff suppressed because it is too large Load Diff