diff --git a/setup.py b/setup.py index 1d4f051..e59cf41 100644 --- a/setup.py +++ b/setup.py @@ -3,65 +3,66 @@ import re from setuptools import find_packages, setup install_requires = [ - 'wagtail>=2.9,<2.17', - 'user-agents>=1.1.0', - 'wagtailfontawesome>=1.1.3', - 'pycountry', + "wagtail>=2.9,<2.17", + "user-agents>=1.1.0", + "wagtailfontawesome>=1.1.3", + "pycountry", ] tests_require = [ - 'factory_boy==2.8.1', - 'flake8-blind-except', - 'flake8-debugger', - 'flake8-isort', - 'flake8', - 'freezegun==0.3.8', - 'pytest-cov==2.5.1', - 'pytest-django==4.1.0', - 'pytest-pythonpath==0.7.2', - 'pytest-sugar==0.9.1', - 'pytest==6.1.2', - 'wagtail_factories==1.1.0', - 'pytest-mock==1.6.3', + "factory_boy==2.8.1", + "flake8-blind-except", + "flake8-debugger", + "flake8-isort", + "flake8", + "freezegun==0.3.8", + "pytest-cov==2.5.1", + "pytest-django==4.1.0", + "pytest-pythonpath==0.7.2", + "pytest-sugar==0.9.1", + "pytest==6.1.2", + "wagtail_factories==1.1.0", + "pytest-mock==1.6.3", ] docs_require = [ - 'sphinx>=1.7.6', - 'sphinx_rtd_theme>=0.4.0', + "sphinx>=1.7.6", + "sphinx_rtd_theme>=0.4.0", ] -with open('README.rst') as fh: +with open("README.rst") as fh: long_description = re.sub( - '^.. start-no-pypi.*^.. end-no-pypi', '', fh.read(), flags=re.M | re.S) + "^.. start-no-pypi.*^.. end-no-pypi", "", fh.read(), flags=re.M | re.S + ) setup( - name='wagtail-personalisation', - version='0.15.3', - description='A Wagtail add-on for showing personalized content', - author='Lab Digital BV and others', - author_email='opensource@labdigital.nl', - url='https://labdigital.nl/', + name="wagtail-personalisation", + version="0.15.3", + description="A Wagtail add-on for showing personalized content", + author="Lab Digital BV and others", + author_email="opensource@labdigital.nl", + url="https://labdigital.nl/", install_requires=install_requires, tests_require=tests_require, extras_require={ - 'docs': docs_require, - 'test': tests_require, + "docs": docs_require, + "test": tests_require, }, - packages=find_packages('src'), - package_dir={'': 'src'}, + packages=find_packages("src"), + package_dir={"": "src"}, include_package_data=True, - license='MIT', + license="MIT", long_description=long_description, classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Environment :: Web Environment', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', - 'Framework :: Django', - 'Framework :: Django :: 2.0', - 'Topic :: Internet :: WWW/HTTP :: Site Management', + "Development Status :: 5 - Production/Stable", + "Environment :: Web Environment", + "Intended Audience :: Developers", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.6", + "Framework :: Django", + "Framework :: Django :: 2.0", + "Topic :: Internet :: WWW/HTTP :: Site Management", ], ) diff --git a/src/wagtail_personalisation/__init__.py b/src/wagtail_personalisation/__init__.py index 3c0c97d..9dd7e9c 100644 --- a/src/wagtail_personalisation/__init__.py +++ b/src/wagtail_personalisation/__init__.py @@ -1 +1 @@ -default_app_config = 'wagtail_personalisation.config.WagtailPersonalisationConfig' +default_app_config = "wagtail_personalisation.config.WagtailPersonalisationConfig" diff --git a/src/wagtail_personalisation/adapters.py b/src/wagtail_personalisation/adapters.py index 509c5c7..fc58272 100644 --- a/src/wagtail_personalisation/adapters.py +++ b/src/wagtail_personalisation/adapters.py @@ -61,18 +61,13 @@ class SessionSegmentsAdapter(BaseSegmentsAdapter): def __init__(self, request): super(SessionSegmentsAdapter, self).__init__(request) - self.request.session.setdefault('segments', []) + self.request.session.setdefault("segments", []) self._segment_cache = None def _segments(self, ids=None): if not ids: ids = [] - segments = ( - Segment.objects - .enabled() - .filter(persistent=True) - .filter(pk__in=ids) - ) + segments = Segment.objects.enabled().filter(persistent=True).filter(pk__in=ids) return segments def get_segments(self, key="segments"): @@ -90,7 +85,7 @@ class SessionSegmentsAdapter(BaseSegmentsAdapter): if key not in self.request.session: return [] raw_segments = self.request.session[key] - segment_ids = [segment['id'] for segment in raw_segments] + segment_ids = [segment["id"] for segment in raw_segments] segments = self._segments(ids=segment_ids) @@ -113,7 +108,7 @@ class SessionSegmentsAdapter(BaseSegmentsAdapter): segment_ids = set() for segment in segments: serialized = create_segment_dictionary(segment) - if serialized['id'] in segment_ids: + if serialized["id"] in segment_ids: continue cache_segments.append(segment) @@ -139,41 +134,44 @@ class SessionSegmentsAdapter(BaseSegmentsAdapter): def add_page_visit(self, page): """Mark the page as visited by the user""" - visit_count = self.request.session.setdefault('visit_count', []) - page_visits = [visit for visit in visit_count if visit['id'] == page.pk] + visit_count = self.request.session.setdefault("visit_count", []) + page_visits = [visit for visit in visit_count if visit["id"] == page.pk] if page_visits: for page_visit in page_visits: - page_visit['count'] += 1 - page_visit['path'] = page.url_path if page else self.request.path + page_visit["count"] += 1 + page_visit["path"] = page.url_path if page else self.request.path self.request.session.modified = True else: - visit_count.append({ - 'slug': page.slug, - 'id': page.pk, - 'path': page.url_path if page else self.request.path, - 'count': 1, - }) + visit_count.append( + { + "slug": page.slug, + "id": page.pk, + "path": page.url_path if page else self.request.path, + "count": 1, + } + ) def get_visit_count(self, page=None): """Return the number of visits on the current request or given page""" path = page.url_path if page else self.request.path - visit_count = self.request.session.setdefault('visit_count', []) + visit_count = self.request.session.setdefault("visit_count", []) for visit in visit_count: - if visit['path'] == path: - return visit['count'] + if visit["path"] == path: + return visit["count"] return 0 def update_visit_count(self): """Update the visit count for all segments in the request session.""" - segments = self.request.session['segments'] - segment_pks = [s['id'] for s in segments] + segments = self.request.session["segments"] + segment_pks = [s["id"] for s in segments] # Update counts - (Segment.objects - .enabled() + ( + Segment.objects.enabled() .filter(pk__in=segment_pks) - .update(visit_count=F('visit_count') + 1)) + .update(visit_count=F("visit_count") + 1) + ) def refresh(self): """Retrieve the request session segments and verify whether or not they @@ -185,27 +183,31 @@ class SessionSegmentsAdapter(BaseSegmentsAdapter): current_segments = self.get_segments() excluded_segments = self.get_segments("excluded_segments") - current_segments = list( - set(current_segments) - set(excluded_segments) - ) + current_segments = list(set(current_segments) - set(excluded_segments)) # Run tests on all remaining enabled segments to verify applicability. additional_segments = [] for segment in enabled_segments: - if segment.is_static and segment.static_users.filter(id=self.request.user.id).exists(): + 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 any( + ( + segment.excluded_users.filter(id=self.request.user.id).exists(), + segment in excluded_segments, + ) + ): continue elif not segment.is_static or not segment.is_full: segment_rules = [] for rule_model in rule_models: segment_rules.extend(rule_model.objects.filter(segment=segment)) - result = self._test_rules(segment_rules, self.request, - match_any=segment.match_any) + result = self._test_rules( + segment_rules, self.request, match_any=segment.match_any + ) if result and segment.randomise_into_segment(): if segment.is_static and not segment.is_full: @@ -223,14 +225,17 @@ class SessionSegmentsAdapter(BaseSegmentsAdapter): self.update_visit_count() -SEGMENT_ADAPTER_CLASS = import_string(getattr( - settings, - 'PERSONALISATION_SEGMENTS_ADAPTER', - 'wagtail_personalisation.adapters.SessionSegmentsAdapter')) +SEGMENT_ADAPTER_CLASS = import_string( + getattr( + settings, + "PERSONALISATION_SEGMENTS_ADAPTER", + "wagtail_personalisation.adapters.SessionSegmentsAdapter", + ) +) def get_segment_adapter(request): """Return the Segment Adapter for the given request""" - if not hasattr(request, 'segment_adapter'): + if not hasattr(request, "segment_adapter"): request.segment_adapter = SEGMENT_ADAPTER_CLASS(request) return request.segment_adapter diff --git a/src/wagtail_personalisation/admin.py b/src/wagtail_personalisation/admin.py index 3ff20ba..fd6f9ab 100644 --- a/src/wagtail_personalisation/admin.py +++ b/src/wagtail_personalisation/admin.py @@ -8,6 +8,7 @@ class UserIsLoggedInRuleAdminInline(admin.TabularInline): administration interface for segments. """ + model = rules.UserIsLoggedInRule @@ -16,6 +17,7 @@ class TimeRuleAdminInline(admin.TabularInline): administration interface for segments. """ + model = rules.TimeRule @@ -24,6 +26,7 @@ class ReferralRuleAdminInline(admin.TabularInline): administration interface for segments. """ + model = rules.ReferralRule @@ -32,13 +35,19 @@ class VisitCountRuleAdminInline(admin.TabularInline): administration interface for segments. """ + model = rules.VisitCountRule class SegmentAdmin(admin.ModelAdmin): """Add the inline models to the Segment admin interface.""" - inlines = (UserIsLoggedInRuleAdminInline, TimeRuleAdminInline, - ReferralRuleAdminInline, VisitCountRuleAdminInline) + + inlines = ( + UserIsLoggedInRuleAdminInline, + TimeRuleAdminInline, + ReferralRuleAdminInline, + VisitCountRuleAdminInline, + ) admin.site.register(models.Segment, SegmentAdmin) diff --git a/src/wagtail_personalisation/admin_urls.py b/src/wagtail_personalisation/admin_urls.py index f6c226a..35151d6 100644 --- a/src/wagtail_personalisation/admin_urls.py +++ b/src/wagtail_personalisation/admin_urls.py @@ -2,15 +2,23 @@ from django.conf.urls import url from wagtail_personalisation import views -app_name = 'segment' +app_name = "segment" urlpatterns = [ - url(r'^segment/(?P[0-9]+)/toggle/$', - views.toggle, name='toggle'), - url(r'^(?P[0-9]+)/copy/(?P[0-9]+)$', - views.copy_page_view, name='copy_page'), - url(r'^segment/toggle_segment_view/$', - views.toggle_segment_view, name='toggle_segment_view'), - url(r'^segment/users/(?P[0-9]+)$', - views.segment_user_data, name='segment_user_data'), + url(r"^segment/(?P[0-9]+)/toggle/$", views.toggle, name="toggle"), + url( + r"^(?P[0-9]+)/copy/(?P[0-9]+)$", + views.copy_page_view, + name="copy_page", + ), + url( + r"^segment/toggle_segment_view/$", + views.toggle_segment_view, + name="toggle_segment_view", + ), + url( + r"^segment/users/(?P[0-9]+)$", + views.segment_user_data, + name="segment_user_data", + ), ] diff --git a/src/wagtail_personalisation/blocks.py b/src/wagtail_personalisation/blocks.py index 85e148e..e0b04d3 100644 --- a/src/wagtail_personalisation/blocks.py +++ b/src/wagtail_personalisation/blocks.py @@ -7,7 +7,7 @@ from wagtail_personalisation.models import Segment def list_segment_choices(): yield -1, ("Show to everyone") - for pk, name in Segment.objects.values_list('pk', 'name'): + for pk, name in Segment.objects.values_list("pk", "name"): yield pk, name @@ -16,8 +16,10 @@ class PersonalisedStructBlock(blocks.StructBlock): segment = blocks.ChoiceBlock( choices=list_segment_choices, - required=False, label=_("Personalisation segment"), - help_text=_("Only show this content block for users in this segment")) + required=False, + label=_("Personalisation segment"), + help_text=_("Only show this content block for users in this segment"), + ) def render(self, value, context=None): """Only render this content block for users in this segment. @@ -30,23 +32,21 @@ class PersonalisedStructBlock(blocks.StructBlock): :rtype: blocks.StructBlock or empty str """ - request = context['request'] + request = context["request"] adapter = get_segment_adapter(request) user_segments = adapter.get_segments() try: - segment_id = int(value['segment']) + segment_id = int(value["segment"]) except (ValueError, TypeError): - return '' + return "" if segment_id > 0: for segment in user_segments: if segment.id == segment_id: - return super(PersonalisedStructBlock, self).render( - value, context) + return super(PersonalisedStructBlock, self).render(value, context) if segment_id == -1: - return super(PersonalisedStructBlock, self).render( - value, context) + return super(PersonalisedStructBlock, self).render(value, context) - return '' + return "" diff --git a/src/wagtail_personalisation/config.py b/src/wagtail_personalisation/config.py index baf1d07..05c4e87 100644 --- a/src/wagtail_personalisation/config.py +++ b/src/wagtail_personalisation/config.py @@ -3,9 +3,9 @@ from django.utils.translation import ugettext_lazy as _ class WagtailPersonalisationConfig(AppConfig): - label = 'wagtail_personalisation' - name = 'wagtail_personalisation' - verbose_name = _('Wagtail Personalisation') + label = "wagtail_personalisation" + name = "wagtail_personalisation" + verbose_name = _("Wagtail Personalisation") def ready(self): from wagtail_personalisation import receivers diff --git a/src/wagtail_personalisation/forms.py b/src/wagtail_personalisation/forms.py index d5a065f..d35e8a5 100644 --- a/src/wagtail_personalisation/forms.py +++ b/src/wagtail_personalisation/forms.py @@ -23,10 +23,8 @@ def user_from_data(user_id): class SegmentAdminForm(WagtailAdminModelForm): - def count_matching_users(self, rules, match_any): - """ Calculates how many users match the given static rules - """ + """Calculates how many users match the given static rules""" count = 0 static_rules = [rule for rule in rules if rule.static] @@ -51,18 +49,28 @@ class SegmentAdminForm(WagtailAdminModelForm): Segment = self._meta.model rules = [ - form.instance for formset in self.formsets.values() + form.instance + for formset in self.formsets.values() for form in formset if form not in formset.deleted_forms ] consistent = rules and Segment.all_static(rules) - if cleaned_data.get('type') == Segment.TYPE_STATIC and not cleaned_data.get('count') and not consistent: - self.add_error('count', _('Static segments with non-static compatible rules must include a count.')) + if ( + cleaned_data.get("type") == Segment.TYPE_STATIC + and not cleaned_data.get("count") + and not consistent + ): + self.add_error( + "count", + _( + "Static segments with non-static compatible rules must include a count." + ), + ) if self.instance.id and self.instance.is_static: if self.has_changed(): - self.add_error_to_fields(self, excluded=['name', 'enabled']) + self.add_error_to_fields(self, excluded=["name", "enabled"]) for formset in self.formsets.values(): if formset.has_changed(): @@ -75,7 +83,7 @@ class SegmentAdminForm(WagtailAdminModelForm): def add_error_to_fields(self, form, excluded=list()): for field in form.changed_data: if field not in excluded: - form.add_error(field, _('Cannot update a static segment')) + form.add_error(field, _("Cannot update a static segment")) def save(self, *args, **kwargs): is_new = not self.instance.id @@ -85,12 +93,14 @@ class SegmentAdminForm(WagtailAdminModelForm): if is_new and self.instance.is_static and not self.instance.all_rules_static: rules = [ - form.instance for formset in self.formsets.values() + form.instance + for formset in self.formsets.values() for form in formset if form not in formset.deleted_forms ] self.instance.matched_users_count = self.count_matching_users( - rules, self.instance.match_any) + rules, self.instance.match_any + ) self.instance.matched_count_updated_at = datetime.now() instance = super(SegmentAdminForm, self).save(*args, **kwargs) @@ -98,7 +108,7 @@ class SegmentAdminForm(WagtailAdminModelForm): if is_new and instance.is_static and instance.all_rules_static: from .adapters import get_segment_adapter - request = RequestFactory().get('/') + request = RequestFactory().get("/") request.session = SessionStore() adapter = get_segment_adapter(request) @@ -111,7 +121,9 @@ class SegmentAdminForm(WagtailAdminModelForm): matched_count = 0 for user in users.iterator(): request.user = user - passes = adapter._test_rules(instance.get_rules(), request, instance.match_any) + passes = adapter._test_rules( + instance.get_rules(), request, instance.match_any + ) if passes: matched_count += 1 if instance.count == 0 or len(users_to_add) < instance.count: @@ -130,7 +142,5 @@ class SegmentAdminForm(WagtailAdminModelForm): @property def media(self): media = super(SegmentAdminForm, self).media - media.add_js( - [static('js/segment_form_control.js')] - ) + media.add_js([static("js/segment_form_control.js")]) return media diff --git a/src/wagtail_personalisation/migrations/0001_initial.py b/src/wagtail_personalisation/migrations/0001_initial.py index 78a13d9..baad8e8 100644 --- a/src/wagtail_personalisation/migrations/0001_initial.py +++ b/src/wagtail_personalisation/migrations/0001_initial.py @@ -12,81 +12,189 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('wagtailcore', '0001_initial'), + ("wagtailcore", "0001_initial"), ] operations = [ migrations.CreateModel( - name='PersonalisablePage', + name="PersonalisablePage", fields=[ - ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Page')), - ('is_segmented', models.BooleanField(default=False)), - ('canonical_page', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='variants', to='wagtail_personalisation.PersonalisablePage')), + ( + "page_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="wagtailcore.Page", + ), + ), + ("is_segmented", models.BooleanField(default=False)), + ( + "canonical_page", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="variants", + to="wagtail_personalisation.PersonalisablePage", + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, - bases=('wagtailcore.page',), + bases=("wagtailcore.page",), ), migrations.CreateModel( - name='ReferralRule', + name="ReferralRule", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('regex_string', models.TextField(verbose_name='Regex string to match the referer with')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "regex_string", + models.TextField( + verbose_name="Regex string to match the referer with" + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, ), migrations.CreateModel( - name='Segment', + name="Segment", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('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(editable=False, null=True)), - ('disable_date', models.DateTimeField(editable=False, null=True)), - ('visit_count', models.PositiveIntegerField(default=0, editable=False)), - ('status', models.CharField(choices=[('enabled', 'Enabled'), ('disabled', 'Disabled')], default='enabled', max_length=20)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("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(editable=False, null=True)), + ("disable_date", models.DateTimeField(editable=False, null=True)), + ("visit_count", models.PositiveIntegerField(default=0, editable=False)), + ( + "status", + models.CharField( + choices=[("enabled", "Enabled"), ("disabled", "Disabled")], + default="enabled", + max_length=20, + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, ), migrations.CreateModel( - name='TimeRule', + name="TimeRule", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('start_time', models.TimeField(verbose_name='Starting time')), - ('end_time', models.TimeField(verbose_name='Ending time')), - ('segment', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='wagtail_personalisation_timerule_related', related_query_name='wagtail_personalisation_timerules', to='wagtail_personalisation.Segment')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("start_time", models.TimeField(verbose_name="Starting time")), + ("end_time", models.TimeField(verbose_name="Ending time")), + ( + "segment", + modelcluster.fields.ParentalKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="wagtail_personalisation_timerule_related", + related_query_name="wagtail_personalisation_timerules", + to="wagtail_personalisation.Segment", + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, ), migrations.CreateModel( - name='VisitCountRule', + name="VisitCountRule", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('operator', models.CharField(choices=[('more_than', 'More than'), ('less_than', 'Less than'), ('equal_to', 'Equal to')], default='ht', max_length=20)), - ('count', models.PositiveSmallIntegerField(default=0, null=True)), - ('counted_page', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='wagtailcore.Page')), - ('segment', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='wagtail_personalisation_visitcountrule_related', related_query_name='wagtail_personalisation_visitcountrules', to='wagtail_personalisation.Segment')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "operator", + models.CharField( + choices=[ + ("more_than", "More than"), + ("less_than", "Less than"), + ("equal_to", "Equal to"), + ], + default="ht", + max_length=20, + ), + ), + ("count", models.PositiveSmallIntegerField(default=0, null=True)), + ( + "counted_page", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="+", + to="wagtailcore.Page", + ), + ), + ( + "segment", + modelcluster.fields.ParentalKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="wagtail_personalisation_visitcountrule_related", + related_query_name="wagtail_personalisation_visitcountrules", + to="wagtail_personalisation.Segment", + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, ), migrations.AddField( - model_name='referralrule', - name='segment', - field=modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='wagtail_personalisation_referralrule_related', related_query_name='wagtail_personalisation_referralrules', to='wagtail_personalisation.Segment'), + model_name="referralrule", + name="segment", + field=modelcluster.fields.ParentalKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="wagtail_personalisation_referralrule_related", + related_query_name="wagtail_personalisation_referralrules", + to="wagtail_personalisation.Segment", + ), ), migrations.AddField( - model_name='personalisablepage', - name='segment', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='segments', to='wagtail_personalisation.Segment'), + model_name="personalisablepage", + name="segment", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="segments", + to="wagtail_personalisation.Segment", + ), ), ] diff --git a/src/wagtail_personalisation/migrations/0002_auto_20161205_1623.py b/src/wagtail_personalisation/migrations/0002_auto_20161205_1623.py index 3142269..bd8e68d 100644 --- a/src/wagtail_personalisation/migrations/0002_auto_20161205_1623.py +++ b/src/wagtail_personalisation/migrations/0002_auto_20161205_1623.py @@ -10,25 +10,57 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('wagtail_personalisation', '0001_initial'), + ("wagtail_personalisation", "0001_initial"), ] operations = [ migrations.CreateModel( - name='QueryRule', + name="QueryRule", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('query_parameter', models.TextField(verbose_name='The query parameter to search for')), - ('query_value', models.TextField(verbose_name='The value of the parameter to match')), - ('segment', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='wagtail_personalisation_queryrule_related', related_query_name='wagtail_personalisation_queryrules', to='wagtail_personalisation.Segment')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "query_parameter", + models.TextField(verbose_name="The query parameter to search for"), + ), + ( + "query_value", + models.TextField( + verbose_name="The value of the parameter to match" + ), + ), + ( + "segment", + modelcluster.fields.ParentalKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="wagtail_personalisation_queryrule_related", + related_query_name="wagtail_personalisation_queryrules", + to="wagtail_personalisation.Segment", + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, ), migrations.AlterField( - model_name='visitcountrule', - name='operator', - field=models.CharField(choices=[('more_than', 'More than'), ('less_than', 'Less than'), ('equal_to', 'Equal to')], default='more_than', max_length=20), + model_name="visitcountrule", + name="operator", + field=models.CharField( + choices=[ + ("more_than", "More than"), + ("less_than", "Less than"), + ("equal_to", "Equal to"), + ], + default="more_than", + max_length=20, + ), ), ] diff --git a/src/wagtail_personalisation/migrations/0003_auto_20161206_1005.py b/src/wagtail_personalisation/migrations/0003_auto_20161206_1005.py index e0933c0..3f206ec 100644 --- a/src/wagtail_personalisation/migrations/0003_auto_20161206_1005.py +++ b/src/wagtail_personalisation/migrations/0003_auto_20161206_1005.py @@ -8,28 +8,36 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('wagtail_personalisation', '0002_auto_20161205_1623'), + ("wagtail_personalisation", "0002_auto_20161205_1623"), ] operations = [ migrations.RemoveField( - model_name='queryrule', - name='query_parameter', + model_name="queryrule", + name="query_parameter", ), migrations.RemoveField( - model_name='queryrule', - name='query_value', + model_name="queryrule", + name="query_value", ), migrations.AddField( - model_name='queryrule', - name='parameter', - field=models.SlugField(default='test', max_length=20, verbose_name='The query parameter to search for'), + model_name="queryrule", + name="parameter", + field=models.SlugField( + default="test", + max_length=20, + verbose_name="The query parameter to search for", + ), preserve_default=False, ), migrations.AddField( - model_name='queryrule', - name='value', - field=models.SlugField(default='test', max_length=20, verbose_name='The value of the parameter to match'), + model_name="queryrule", + name="value", + field=models.SlugField( + default="test", + max_length=20, + verbose_name="The value of the parameter to match", + ), preserve_default=False, ), ] diff --git a/src/wagtail_personalisation/migrations/0004_segment_persistent.py b/src/wagtail_personalisation/migrations/0004_segment_persistent.py index a4f89f8..f5e592b 100644 --- a/src/wagtail_personalisation/migrations/0004_segment_persistent.py +++ b/src/wagtail_personalisation/migrations/0004_segment_persistent.py @@ -8,13 +8,15 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('wagtail_personalisation', '0003_auto_20161206_1005'), + ("wagtail_personalisation", "0003_auto_20161206_1005"), ] operations = [ migrations.AddField( - model_name='segment', - name='persistent', - field=models.BooleanField(default=False, help_text='Should the segment persist between visits?'), + model_name="segment", + name="persistent", + field=models.BooleanField( + default=False, help_text="Should the segment persist between visits?" + ), ), ] diff --git a/src/wagtail_personalisation/migrations/0005_userisloggedinrule.py b/src/wagtail_personalisation/migrations/0005_userisloggedinrule.py index b361d18..dccd67b 100644 --- a/src/wagtail_personalisation/migrations/0005_userisloggedinrule.py +++ b/src/wagtail_personalisation/migrations/0005_userisloggedinrule.py @@ -10,19 +10,35 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('wagtail_personalisation', '0004_segment_persistent'), + ("wagtail_personalisation", "0004_segment_persistent"), ] operations = [ migrations.CreateModel( - name='UserIsLoggedInRule', + name="UserIsLoggedInRule", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('is_logged_in', models.BooleanField(default=False)), - ('segment', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='wagtail_personalisation_userisloggedinrule_related', related_query_name='wagtail_personalisation_userisloggedinrules', to='wagtail_personalisation.Segment')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("is_logged_in", models.BooleanField(default=False)), + ( + "segment", + modelcluster.fields.ParentalKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="wagtail_personalisation_userisloggedinrule_related", + related_query_name="wagtail_personalisation_userisloggedinrules", + to="wagtail_personalisation.Segment", + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, ), ] diff --git a/src/wagtail_personalisation/migrations/0006_segment_match_any.py b/src/wagtail_personalisation/migrations/0006_segment_match_any.py index 99bd98f..1507d12 100644 --- a/src/wagtail_personalisation/migrations/0006_segment_match_any.py +++ b/src/wagtail_personalisation/migrations/0006_segment_match_any.py @@ -8,13 +8,16 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('wagtail_personalisation', '0005_userisloggedinrule'), + ("wagtail_personalisation", "0005_userisloggedinrule"), ] operations = [ migrations.AddField( - model_name='segment', - name='match_any', - field=models.BooleanField(default=False, help_text='Should the segment match all the rules or just one of them?'), + model_name="segment", + name="match_any", + field=models.BooleanField( + default=False, + help_text="Should the segment match all the rules or just one of them?", + ), ), ] diff --git a/src/wagtail_personalisation/migrations/0007_dayrule.py b/src/wagtail_personalisation/migrations/0007_dayrule.py index 7bfa79f..f89a07f 100644 --- a/src/wagtail_personalisation/migrations/0007_dayrule.py +++ b/src/wagtail_personalisation/migrations/0007_dayrule.py @@ -10,25 +10,41 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('wagtail_personalisation', '0006_segment_match_any'), + ("wagtail_personalisation", "0006_segment_match_any"), ] operations = [ migrations.CreateModel( - name='DayRule', + name="DayRule", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('mon', models.BooleanField(default=False, verbose_name='Monday')), - ('tue', models.BooleanField(default=False, verbose_name='Tuesday')), - ('wed', models.BooleanField(default=False, verbose_name='Wednesday')), - ('thu', models.BooleanField(default=False, verbose_name='Thursday')), - ('fri', models.BooleanField(default=False, verbose_name='Friday')), - ('sat', models.BooleanField(default=False, verbose_name='Saturday')), - ('sun', models.BooleanField(default=False, verbose_name='Sunday')), - ('segment', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='wagtail_personalisation_dayrule_related', related_query_name='wagtail_personalisation_dayrules', to='wagtail_personalisation.Segment')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("mon", models.BooleanField(default=False, verbose_name="Monday")), + ("tue", models.BooleanField(default=False, verbose_name="Tuesday")), + ("wed", models.BooleanField(default=False, verbose_name="Wednesday")), + ("thu", models.BooleanField(default=False, verbose_name="Thursday")), + ("fri", models.BooleanField(default=False, verbose_name="Friday")), + ("sat", models.BooleanField(default=False, verbose_name="Saturday")), + ("sun", models.BooleanField(default=False, verbose_name="Sunday")), + ( + "segment", + modelcluster.fields.ParentalKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="wagtail_personalisation_dayrule_related", + related_query_name="wagtail_personalisation_dayrules", + to="wagtail_personalisation.Segment", + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, ), ] diff --git a/src/wagtail_personalisation/migrations/0008_devicerule.py b/src/wagtail_personalisation/migrations/0008_devicerule.py index 448985b..6f60589 100644 --- a/src/wagtail_personalisation/migrations/0008_devicerule.py +++ b/src/wagtail_personalisation/migrations/0008_devicerule.py @@ -10,21 +10,40 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('wagtail_personalisation', '0007_dayrule'), + ("wagtail_personalisation", "0007_dayrule"), ] operations = [ migrations.CreateModel( - name='DeviceRule', + name="DeviceRule", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('mobile', models.BooleanField(default=False, verbose_name='Mobile phone')), - ('tablet', models.BooleanField(default=False, verbose_name='Tablet')), - ('desktop', models.BooleanField(default=False, verbose_name='Desktop')), - ('segment', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='wagtail_personalisation_devicerule_related', related_query_name='wagtail_personalisation_devicerules', to='wagtail_personalisation.Segment')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "mobile", + models.BooleanField(default=False, verbose_name="Mobile phone"), + ), + ("tablet", models.BooleanField(default=False, verbose_name="Tablet")), + ("desktop", models.BooleanField(default=False, verbose_name="Desktop")), + ( + "segment", + modelcluster.fields.ParentalKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="wagtail_personalisation_devicerule_related", + related_query_name="wagtail_personalisation_devicerules", + to="wagtail_personalisation.Segment", + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, ), ] diff --git a/src/wagtail_personalisation/migrations/0009_auto_20170531_0428.py b/src/wagtail_personalisation/migrations/0009_auto_20170531_0428.py index 1f64e98..e85d1ec 100644 --- a/src/wagtail_personalisation/migrations/0009_auto_20170531_0428.py +++ b/src/wagtail_personalisation/migrations/0009_auto_20170531_0428.py @@ -8,23 +8,23 @@ from django.db import migrations class Migration(migrations.Migration): dependencies = [ - ('wagtail_personalisation', '0008_devicerule'), + ("wagtail_personalisation", "0008_devicerule"), ] operations = [ migrations.RemoveField( - model_name='personalisablepage', - name='canonical_page', + model_name="personalisablepage", + name="canonical_page", ), migrations.RemoveField( - model_name='personalisablepage', - name='page_ptr', + model_name="personalisablepage", + name="page_ptr", ), migrations.RemoveField( - model_name='personalisablepage', - name='segment', + model_name="personalisablepage", + name="segment", ), migrations.DeleteModel( - name='PersonalisablePage', + name="PersonalisablePage", ), ] diff --git a/src/wagtail_personalisation/migrations/0010_auto_20170531_1101.py b/src/wagtail_personalisation/migrations/0010_auto_20170531_1101.py index 289f8c3..07cf91a 100644 --- a/src/wagtail_personalisation/migrations/0010_auto_20170531_1101.py +++ b/src/wagtail_personalisation/migrations/0010_auto_20170531_1101.py @@ -8,41 +8,43 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('wagtail_personalisation', '0009_auto_20170531_0428'), + ("wagtail_personalisation", "0009_auto_20170531_0428"), ] operations = [ migrations.AlterModelOptions( - name='dayrule', - options={'verbose_name': 'Day Rule'}, + name="dayrule", + options={"verbose_name": "Day Rule"}, ), migrations.AlterModelOptions( - name='devicerule', - options={'verbose_name': 'Device Rule'}, + name="devicerule", + options={"verbose_name": "Device Rule"}, ), migrations.AlterModelOptions( - name='queryrule', - options={'verbose_name': 'Query Rule'}, + name="queryrule", + options={"verbose_name": "Query Rule"}, ), migrations.AlterModelOptions( - name='referralrule', - options={'verbose_name': 'Referral Rule'}, + name="referralrule", + options={"verbose_name": "Referral Rule"}, ), migrations.AlterModelOptions( - name='timerule', - options={'verbose_name': 'Time Rule'}, + name="timerule", + options={"verbose_name": "Time Rule"}, ), migrations.AlterModelOptions( - name='userisloggedinrule', - options={'verbose_name': 'Logged in Rule'}, + name="userisloggedinrule", + options={"verbose_name": "Logged in Rule"}, ), migrations.AlterModelOptions( - name='visitcountrule', - options={'verbose_name': 'Visit count Rule'}, + name="visitcountrule", + options={"verbose_name": "Visit count Rule"}, ), migrations.AlterField( - model_name='referralrule', - name='regex_string', - field=models.TextField(verbose_name='Regular expression to match the referrer'), + model_name="referralrule", + name="regex_string", + field=models.TextField( + verbose_name="Regular expression to match the referrer" + ), ), ] diff --git a/src/wagtail_personalisation/migrations/0011_personalisablepagemetadata.py b/src/wagtail_personalisation/migrations/0011_personalisablepagemetadata.py index a4834f5..14d74b7 100644 --- a/src/wagtail_personalisation/migrations/0011_personalisablepagemetadata.py +++ b/src/wagtail_personalisation/migrations/0011_personalisablepagemetadata.py @@ -9,22 +9,55 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('wagtailcore', '0001_initial'), - ('wagtail_personalisation', '0010_auto_20170531_1101'), + ("wagtailcore", "0001_initial"), + ("wagtail_personalisation", "0010_auto_20170531_1101"), ] operations = [ migrations.CreateModel( - name='PersonalisablePageMetadata', + name="PersonalisablePageMetadata", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('is_segmented', models.BooleanField(default=False)), - ('canonical_page', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='personalisable_canonical_metadata', to='wagtailcore.Page')), - ('segment', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='page_metadata', to='wagtail_personalisation.Segment')), - ('variant', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='_personalisable_page_metadata', to='wagtailcore.Page')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("is_segmented", models.BooleanField(default=False)), + ( + "canonical_page", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="personalisable_canonical_metadata", + to="wagtailcore.Page", + ), + ), + ( + "segment", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="page_metadata", + to="wagtail_personalisation.Segment", + ), + ), + ( + "variant", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name="_personalisable_page_metadata", + to="wagtailcore.Page", + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, ), ] diff --git a/src/wagtail_personalisation/migrations/0012_remove_personalisablepagemetadata_is_segmented.py b/src/wagtail_personalisation/migrations/0012_remove_personalisablepagemetadata_is_segmented.py index b8e2438..2582599 100644 --- a/src/wagtail_personalisation/migrations/0012_remove_personalisablepagemetadata_is_segmented.py +++ b/src/wagtail_personalisation/migrations/0012_remove_personalisablepagemetadata_is_segmented.py @@ -8,12 +8,12 @@ from django.db import migrations class Migration(migrations.Migration): dependencies = [ - ('wagtail_personalisation', '0011_personalisablepagemetadata'), + ("wagtail_personalisation", "0011_personalisablepagemetadata"), ] operations = [ migrations.RemoveField( - model_name='personalisablepagemetadata', - name='is_segmented', + model_name="personalisablepagemetadata", + name="is_segmented", ), ] diff --git a/src/wagtail_personalisation/migrations/0013_add_dynamic_static_to_segment.py b/src/wagtail_personalisation/migrations/0013_add_dynamic_static_to_segment.py index 6196fa8..ff52165 100644 --- a/src/wagtail_personalisation/migrations/0013_add_dynamic_static_to_segment.py +++ b/src/wagtail_personalisation/migrations/0013_add_dynamic_static_to_segment.py @@ -8,24 +8,35 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('sessions', '0001_initial'), - ('wagtail_personalisation', '0012_remove_personalisablepagemetadata_is_segmented'), + ("sessions", "0001_initial"), + ( + "wagtail_personalisation", + "0012_remove_personalisablepagemetadata_is_segmented", + ), ] operations = [ migrations.AddField( - model_name='segment', - name='count', - field=models.PositiveSmallIntegerField(default=0, help_text='If this number is set for a static segment users will be added to the set until the number is reached. After this no more users will be added.'), + model_name="segment", + name="count", + field=models.PositiveSmallIntegerField( + default=0, + help_text="If this number is set for a static segment users will be added to the set until the number is reached. After this no more users will be added.", + ), ), migrations.AddField( - model_name='segment', - name='sessions', - field=models.ManyToManyField(to='sessions.Session'), + model_name="segment", + name="sessions", + field=models.ManyToManyField(to="sessions.Session"), ), migrations.AddField( - model_name='segment', - name='type', - field=models.CharField(choices=[('dynamic', 'Dynamic'), ('static', 'Static')], default='dynamic', help_text='\n

Dynamic: Users in this segment will change\n as more or less meet the rules specified in the segment.\n
Static: If the segment contains only static\n compatible rules the segment will contain the members that pass\n those rules when the segment is created. Mixed static segments or\n those containing entirely non static compatible rules will be\n populated using the count variable.\n ', max_length=20), + model_name="segment", + name="type", + field=models.CharField( + choices=[("dynamic", "Dynamic"), ("static", "Static")], + default="dynamic", + help_text="\n

Dynamic: Users in this segment will change\n as more or less meet the rules specified in the segment.\n
Static: If the segment contains only static\n compatible rules the segment will contain the members that pass\n those rules when the segment is created. Mixed static segments or\n those containing entirely non static compatible rules will be\n populated using the count variable.\n ", + max_length=20, + ), ), ] diff --git a/src/wagtail_personalisation/migrations/0015_static_users.py b/src/wagtail_personalisation/migrations/0015_static_users.py index ea76aa8..c2f328a 100644 --- a/src/wagtail_personalisation/migrations/0015_static_users.py +++ b/src/wagtail_personalisation/migrations/0015_static_users.py @@ -10,17 +10,17 @@ class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('wagtail_personalisation', '0013_add_dynamic_static_to_segment'), + ("wagtail_personalisation", "0013_add_dynamic_static_to_segment"), ] operations = [ migrations.RemoveField( - model_name='segment', - name='sessions', + model_name="segment", + name="sessions", ), migrations.AddField( - model_name='segment', - name='static_users', + model_name="segment", + name="static_users", field=models.ManyToManyField(to=settings.AUTH_USER_MODEL), ), ] diff --git a/src/wagtail_personalisation/migrations/0016_auto_20180125_0918.py b/src/wagtail_personalisation/migrations/0016_auto_20180125_0918.py index ae7bdd1..a553b8a 100644 --- a/src/wagtail_personalisation/migrations/0016_auto_20180125_0918.py +++ b/src/wagtail_personalisation/migrations/0016_auto_20180125_0918.py @@ -8,18 +8,18 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('wagtail_personalisation', '0015_static_users'), + ("wagtail_personalisation", "0015_static_users"), ] operations = [ migrations.AddField( - model_name='segment', - name='matched_count_updated_at', + model_name="segment", + name="matched_count_updated_at", field=models.DateTimeField(editable=False, null=True), ), migrations.AddField( - model_name='segment', - name='matched_users_count', + model_name="segment", + name="matched_users_count", field=models.PositiveIntegerField(default=0, editable=False), ), ] diff --git a/src/wagtail_personalisation/migrations/0017_segment_randomisation_percent.py b/src/wagtail_personalisation/migrations/0017_segment_randomisation_percent.py index bd68335..f637869 100644 --- a/src/wagtail_personalisation/migrations/0017_segment_randomisation_percent.py +++ b/src/wagtail_personalisation/migrations/0017_segment_randomisation_percent.py @@ -9,13 +9,22 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('wagtail_personalisation', '0016_auto_20180125_0918'), + ("wagtail_personalisation", "0016_auto_20180125_0918"), ] operations = [ migrations.AddField( - model_name='segment', - name='randomisation_percent', - field=models.PositiveSmallIntegerField(blank=True, default=None, help_text='If this number is set each user matching the rules will have this percentage chance of being placed in the segment.', null=True, validators=[django.core.validators.MaxValueValidator(100), django.core.validators.MinValueValidator(0)]), + model_name="segment", + name="randomisation_percent", + field=models.PositiveSmallIntegerField( + blank=True, + default=None, + help_text="If this number is set each user matching the rules will have this percentage chance of being placed in the segment.", + null=True, + validators=[ + django.core.validators.MaxValueValidator(100), + django.core.validators.MinValueValidator(0), + ], + ), ), ] diff --git a/src/wagtail_personalisation/migrations/0018_segment_excluded_users.py b/src/wagtail_personalisation/migrations/0018_segment_excluded_users.py index bafa477..3b739a9 100644 --- a/src/wagtail_personalisation/migrations/0018_segment_excluded_users.py +++ b/src/wagtail_personalisation/migrations/0018_segment_excluded_users.py @@ -10,13 +10,17 @@ class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('wagtail_personalisation', '0017_segment_randomisation_percent'), + ("wagtail_personalisation", "0017_segment_randomisation_percent"), ] operations = [ migrations.AddField( - model_name='segment', - name='excluded_users', - field=models.ManyToManyField(help_text='Users that matched the rules but were excluded from the segment for some reason e.g. randomisation', related_name='excluded_segments', to=settings.AUTH_USER_MODEL), + model_name="segment", + name="excluded_users", + field=models.ManyToManyField( + help_text="Users that matched the rules but were excluded from the segment for some reason e.g. randomisation", + related_name="excluded_segments", + to=settings.AUTH_USER_MODEL, + ), ), ] diff --git a/src/wagtail_personalisation/migrations/0019_auto_20180526_1425.py b/src/wagtail_personalisation/migrations/0019_auto_20180526_1425.py index 492fc11..edb00ce 100644 --- a/src/wagtail_personalisation/migrations/0019_auto_20180526_1425.py +++ b/src/wagtail_personalisation/migrations/0019_auto_20180526_1425.py @@ -7,13 +7,19 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('wagtail_personalisation', '0018_segment_excluded_users'), + ("wagtail_personalisation", "0018_segment_excluded_users"), ] operations = [ migrations.AlterField( - model_name='personalisablepagemetadata', - name='segment', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='page_metadata', to='wagtail_personalisation.Segment'), + model_name="personalisablepagemetadata", + name="segment", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="page_metadata", + to="wagtail_personalisation.Segment", + ), ), ] diff --git a/src/wagtail_personalisation/migrations/0020_rules_delete_relatedqueryname.py b/src/wagtail_personalisation/migrations/0020_rules_delete_relatedqueryname.py index 88d811d..db2e322 100644 --- a/src/wagtail_personalisation/migrations/0020_rules_delete_relatedqueryname.py +++ b/src/wagtail_personalisation/migrations/0020_rules_delete_relatedqueryname.py @@ -8,43 +8,71 @@ from django.db import migrations class Migration(migrations.Migration): dependencies = [ - ('wagtail_personalisation', '0019_auto_20180526_1425'), + ("wagtail_personalisation", "0019_auto_20180526_1425"), ] operations = [ migrations.AlterField( - model_name='dayrule', - name='segment', - field=modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='wagtail_personalisation_dayrules', to='wagtail_personalisation.Segment'), + model_name="dayrule", + name="segment", + field=modelcluster.fields.ParentalKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="wagtail_personalisation_dayrules", + to="wagtail_personalisation.Segment", + ), ), migrations.AlterField( - model_name='devicerule', - name='segment', - field=modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='wagtail_personalisation_devicerules', to='wagtail_personalisation.Segment'), + model_name="devicerule", + name="segment", + field=modelcluster.fields.ParentalKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="wagtail_personalisation_devicerules", + to="wagtail_personalisation.Segment", + ), ), migrations.AlterField( - model_name='queryrule', - name='segment', - field=modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='wagtail_personalisation_queryrules', to='wagtail_personalisation.Segment'), + model_name="queryrule", + name="segment", + field=modelcluster.fields.ParentalKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="wagtail_personalisation_queryrules", + to="wagtail_personalisation.Segment", + ), ), migrations.AlterField( - model_name='referralrule', - name='segment', - field=modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='wagtail_personalisation_referralrules', to='wagtail_personalisation.Segment'), + model_name="referralrule", + name="segment", + field=modelcluster.fields.ParentalKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="wagtail_personalisation_referralrules", + to="wagtail_personalisation.Segment", + ), ), migrations.AlterField( - model_name='timerule', - name='segment', - field=modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='wagtail_personalisation_timerules', to='wagtail_personalisation.Segment'), + model_name="timerule", + name="segment", + field=modelcluster.fields.ParentalKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="wagtail_personalisation_timerules", + to="wagtail_personalisation.Segment", + ), ), migrations.AlterField( - model_name='userisloggedinrule', - name='segment', - field=modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='wagtail_personalisation_userisloggedinrules', to='wagtail_personalisation.Segment'), + model_name="userisloggedinrule", + name="segment", + field=modelcluster.fields.ParentalKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="wagtail_personalisation_userisloggedinrules", + to="wagtail_personalisation.Segment", + ), ), migrations.AlterField( - model_name='visitcountrule', - name='segment', - field=modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='wagtail_personalisation_visitcountrules', to='wagtail_personalisation.Segment'), + model_name="visitcountrule", + name="segment", + field=modelcluster.fields.ParentalKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="wagtail_personalisation_visitcountrules", + to="wagtail_personalisation.Segment", + ), ), ] diff --git a/src/wagtail_personalisation/migrations/0021_personalisablepagemetadata_segment_set_on_delete_protect.py b/src/wagtail_personalisation/migrations/0021_personalisablepagemetadata_segment_set_on_delete_protect.py index 7608bf6..454437d 100644 --- a/src/wagtail_personalisation/migrations/0021_personalisablepagemetadata_segment_set_on_delete_protect.py +++ b/src/wagtail_personalisation/migrations/0021_personalisablepagemetadata_segment_set_on_delete_protect.py @@ -7,13 +7,18 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('wagtail_personalisation', '0020_rules_delete_relatedqueryname'), + ("wagtail_personalisation", "0020_rules_delete_relatedqueryname"), ] operations = [ migrations.AlterField( - model_name='personalisablepagemetadata', - name='segment', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='page_metadata', to='wagtail_personalisation.Segment'), + model_name="personalisablepagemetadata", + name="segment", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="page_metadata", + to="wagtail_personalisation.Segment", + ), ), ] diff --git a/src/wagtail_personalisation/migrations/0022_personalisablepagemetadata_canonical_protect.py b/src/wagtail_personalisation/migrations/0022_personalisablepagemetadata_canonical_protect.py index 6dbf745..6a24de0 100644 --- a/src/wagtail_personalisation/migrations/0022_personalisablepagemetadata_canonical_protect.py +++ b/src/wagtail_personalisation/migrations/0022_personalisablepagemetadata_canonical_protect.py @@ -7,13 +7,21 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('wagtail_personalisation', '0021_personalisablepagemetadata_segment_set_on_delete_protect'), + ( + "wagtail_personalisation", + "0021_personalisablepagemetadata_segment_set_on_delete_protect", + ), ] operations = [ migrations.AlterField( - model_name='personalisablepagemetadata', - name='canonical_page', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='personalisable_canonical_metadata', to='wagtailcore.Page'), + model_name="personalisablepagemetadata", + name="canonical_page", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="personalisable_canonical_metadata", + to="wagtailcore.Page", + ), ), ] diff --git a/src/wagtail_personalisation/migrations/0023_personalisablepagemetadata_variant_cascade.py b/src/wagtail_personalisation/migrations/0023_personalisablepagemetadata_variant_cascade.py index 7dcaf87..56f9bf1 100644 --- a/src/wagtail_personalisation/migrations/0023_personalisablepagemetadata_variant_cascade.py +++ b/src/wagtail_personalisation/migrations/0023_personalisablepagemetadata_variant_cascade.py @@ -7,13 +7,21 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('wagtail_personalisation', '0022_personalisablepagemetadata_canonical_protect'), + ( + "wagtail_personalisation", + "0022_personalisablepagemetadata_canonical_protect", + ), ] operations = [ migrations.AlterField( - model_name='personalisablepagemetadata', - name='variant', - field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='_personalisable_page_metadata', to='wagtailcore.Page'), + model_name="personalisablepagemetadata", + name="variant", + field=models.OneToOneField( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="_personalisable_page_metadata", + to="wagtailcore.Page", + ), ), ] diff --git a/src/wagtail_personalisation/migrations/0024_origincountryrule.py b/src/wagtail_personalisation/migrations/0024_origincountryrule.py index b6a9aa0..fa180ac 100644 --- a/src/wagtail_personalisation/migrations/0024_origincountryrule.py +++ b/src/wagtail_personalisation/migrations/0024_origincountryrule.py @@ -8,19 +8,291 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('wagtail_personalisation', '0023_personalisablepagemetadata_variant_cascade'), + ("wagtail_personalisation", "0023_personalisablepagemetadata_variant_cascade"), ] operations = [ migrations.CreateModel( - name='OriginCountryRule', + name="OriginCountryRule", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('country', models.CharField(choices=[('aw', 'Aruba'), ('af', 'Afghanistan'), ('ao', 'Angola'), ('ai', 'Anguilla'), ('ax', 'Åland Islands'), ('al', 'Albania'), ('ad', 'Andorra'), ('ae', 'United Arab Emirates'), ('ar', 'Argentina'), ('am', 'Armenia'), ('as', 'American Samoa'), ('aq', 'Antarctica'), ('tf', 'French Southern Territories'), ('ag', 'Antigua and Barbuda'), ('au', 'Australia'), ('at', 'Austria'), ('az', 'Azerbaijan'), ('bi', 'Burundi'), ('be', 'Belgium'), ('bj', 'Benin'), ('bq', 'Bonaire, Sint Eustatius and Saba'), ('bf', 'Burkina Faso'), ('bd', 'Bangladesh'), ('bg', 'Bulgaria'), ('bh', 'Bahrain'), ('bs', 'Bahamas'), ('ba', 'Bosnia and Herzegovina'), ('bl', 'Saint Barthélemy'), ('by', 'Belarus'), ('bz', 'Belize'), ('bm', 'Bermuda'), ('bo', 'Bolivia, Plurinational State of'), ('br', 'Brazil'), ('bb', 'Barbados'), ('bn', 'Brunei Darussalam'), ('bt', 'Bhutan'), ('bv', 'Bouvet Island'), ('bw', 'Botswana'), ('cf', 'Central African Republic'), ('ca', 'Canada'), ('cc', 'Cocos (Keeling) Islands'), ('ch', 'Switzerland'), ('cl', 'Chile'), ('cn', 'China'), ('ci', "Côte d'Ivoire"), ('cm', 'Cameroon'), ('cd', 'Congo, The Democratic Republic of the'), ('cg', 'Congo'), ('ck', 'Cook Islands'), ('co', 'Colombia'), ('km', 'Comoros'), ('cv', 'Cabo Verde'), ('cr', 'Costa Rica'), ('cu', 'Cuba'), ('cw', 'Curaçao'), ('cx', 'Christmas Island'), ('ky', 'Cayman Islands'), ('cy', 'Cyprus'), ('cz', 'Czechia'), ('de', 'Germany'), ('dj', 'Djibouti'), ('dm', 'Dominica'), ('dk', 'Denmark'), ('do', 'Dominican Republic'), ('dz', 'Algeria'), ('ec', 'Ecuador'), ('eg', 'Egypt'), ('er', 'Eritrea'), ('eh', 'Western Sahara'), ('es', 'Spain'), ('ee', 'Estonia'), ('et', 'Ethiopia'), ('fi', 'Finland'), ('fj', 'Fiji'), ('fk', 'Falkland Islands (Malvinas)'), ('fr', 'France'), ('fo', 'Faroe Islands'), ('fm', 'Micronesia, Federated States of'), ('ga', 'Gabon'), ('gb', 'United Kingdom'), ('ge', 'Georgia'), ('gg', 'Guernsey'), ('gh', 'Ghana'), ('gi', 'Gibraltar'), ('gn', 'Guinea'), ('gp', 'Guadeloupe'), ('gm', 'Gambia'), ('gw', 'Guinea-Bissau'), ('gq', 'Equatorial Guinea'), ('gr', 'Greece'), ('gd', 'Grenada'), ('gl', 'Greenland'), ('gt', 'Guatemala'), ('gf', 'French Guiana'), ('gu', 'Guam'), ('gy', 'Guyana'), ('hk', 'Hong Kong'), ('hm', 'Heard Island and McDonald Islands'), ('hn', 'Honduras'), ('hr', 'Croatia'), ('ht', 'Haiti'), ('hu', 'Hungary'), ('id', 'Indonesia'), ('im', 'Isle of Man'), ('in', 'India'), ('io', 'British Indian Ocean Territory'), ('ie', 'Ireland'), ('ir', 'Iran, Islamic Republic of'), ('iq', 'Iraq'), ('is', 'Iceland'), ('il', 'Israel'), ('it', 'Italy'), ('jm', 'Jamaica'), ('je', 'Jersey'), ('jo', 'Jordan'), ('jp', 'Japan'), ('kz', 'Kazakhstan'), ('ke', 'Kenya'), ('kg', 'Kyrgyzstan'), ('kh', 'Cambodia'), ('ki', 'Kiribati'), ('kn', 'Saint Kitts and Nevis'), ('kr', 'Korea, Republic of'), ('kw', 'Kuwait'), ('la', "Lao People's Democratic Republic"), ('lb', 'Lebanon'), ('lr', 'Liberia'), ('ly', 'Libya'), ('lc', 'Saint Lucia'), ('li', 'Liechtenstein'), ('lk', 'Sri Lanka'), ('ls', 'Lesotho'), ('lt', 'Lithuania'), ('lu', 'Luxembourg'), ('lv', 'Latvia'), ('mo', 'Macao'), ('mf', 'Saint Martin (French part)'), ('ma', 'Morocco'), ('mc', 'Monaco'), ('md', 'Moldova, Republic of'), ('mg', 'Madagascar'), ('mv', 'Maldives'), ('mx', 'Mexico'), ('mh', 'Marshall Islands'), ('mk', 'Macedonia, Republic of'), ('ml', 'Mali'), ('mt', 'Malta'), ('mm', 'Myanmar'), ('me', 'Montenegro'), ('mn', 'Mongolia'), ('mp', 'Northern Mariana Islands'), ('mz', 'Mozambique'), ('mr', 'Mauritania'), ('ms', 'Montserrat'), ('mq', 'Martinique'), ('mu', 'Mauritius'), ('mw', 'Malawi'), ('my', 'Malaysia'), ('yt', 'Mayotte'), ('na', 'Namibia'), ('nc', 'New Caledonia'), ('ne', 'Niger'), ('nf', 'Norfolk Island'), ('ng', 'Nigeria'), ('ni', 'Nicaragua'), ('nu', 'Niue'), ('nl', 'Netherlands'), ('no', 'Norway'), ('np', 'Nepal'), ('nr', 'Nauru'), ('nz', 'New Zealand'), ('om', 'Oman'), ('pk', 'Pakistan'), ('pa', 'Panama'), ('pn', 'Pitcairn'), ('pe', 'Peru'), ('ph', 'Philippines'), ('pw', 'Palau'), ('pg', 'Papua New Guinea'), ('pl', 'Poland'), ('pr', 'Puerto Rico'), ('kp', "Korea, Democratic People's Republic of"), ('pt', 'Portugal'), ('py', 'Paraguay'), ('ps', 'Palestine, State of'), ('pf', 'French Polynesia'), ('qa', 'Qatar'), ('re', 'Réunion'), ('ro', 'Romania'), ('ru', 'Russian Federation'), ('rw', 'Rwanda'), ('sa', 'Saudi Arabia'), ('sd', 'Sudan'), ('sn', 'Senegal'), ('sg', 'Singapore'), ('gs', 'South Georgia and the South Sandwich Islands'), ('sh', 'Saint Helena, Ascension and Tristan da Cunha'), ('sj', 'Svalbard and Jan Mayen'), ('sb', 'Solomon Islands'), ('sl', 'Sierra Leone'), ('sv', 'El Salvador'), ('sm', 'San Marino'), ('so', 'Somalia'), ('pm', 'Saint Pierre and Miquelon'), ('rs', 'Serbia'), ('ss', 'South Sudan'), ('st', 'Sao Tome and Principe'), ('sr', 'Suriname'), ('sk', 'Slovakia'), ('si', 'Slovenia'), ('se', 'Sweden'), ('sz', 'Swaziland'), ('sx', 'Sint Maarten (Dutch part)'), ('sc', 'Seychelles'), ('sy', 'Syrian Arab Republic'), ('tc', 'Turks and Caicos Islands'), ('td', 'Chad'), ('tg', 'Togo'), ('th', 'Thailand'), ('tj', 'Tajikistan'), ('tk', 'Tokelau'), ('tm', 'Turkmenistan'), ('tl', 'Timor-Leste'), ('to', 'Tonga'), ('tt', 'Trinidad and Tobago'), ('tn', 'Tunisia'), ('tr', 'Turkey'), ('tv', 'Tuvalu'), ('tw', 'Taiwan, Province of China'), ('tz', 'Tanzania, United Republic of'), ('ug', 'Uganda'), ('ua', 'Ukraine'), ('um', 'United States Minor Outlying Islands'), ('uy', 'Uruguay'), ('us', 'United States'), ('uz', 'Uzbekistan'), ('va', 'Holy See (Vatican City State)'), ('vc', 'Saint Vincent and the Grenadines'), ('ve', 'Venezuela, Bolivarian Republic of'), ('vg', 'Virgin Islands, British'), ('vi', 'Virgin Islands, U.S.'), ('vn', 'Viet Nam'), ('vu', 'Vanuatu'), ('wf', 'Wallis and Futuna'), ('ws', 'Samoa'), ('ye', 'Yemen'), ('za', 'South Africa'), ('zm', 'Zambia'), ('zw', 'Zimbabwe')], help_text='Select origin country of the request that this rule will match against. This rule will only work if you use Cloudflare or CloudFront IP geolocation or if GeoIP2 module is configured.', max_length=2)), - ('segment', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='wagtail_personalisation_origincountryrules', to='wagtail_personalisation.Segment')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "country", + models.CharField( + choices=[ + ("aw", "Aruba"), + ("af", "Afghanistan"), + ("ao", "Angola"), + ("ai", "Anguilla"), + ("ax", "Åland Islands"), + ("al", "Albania"), + ("ad", "Andorra"), + ("ae", "United Arab Emirates"), + ("ar", "Argentina"), + ("am", "Armenia"), + ("as", "American Samoa"), + ("aq", "Antarctica"), + ("tf", "French Southern Territories"), + ("ag", "Antigua and Barbuda"), + ("au", "Australia"), + ("at", "Austria"), + ("az", "Azerbaijan"), + ("bi", "Burundi"), + ("be", "Belgium"), + ("bj", "Benin"), + ("bq", "Bonaire, Sint Eustatius and Saba"), + ("bf", "Burkina Faso"), + ("bd", "Bangladesh"), + ("bg", "Bulgaria"), + ("bh", "Bahrain"), + ("bs", "Bahamas"), + ("ba", "Bosnia and Herzegovina"), + ("bl", "Saint Barthélemy"), + ("by", "Belarus"), + ("bz", "Belize"), + ("bm", "Bermuda"), + ("bo", "Bolivia, Plurinational State of"), + ("br", "Brazil"), + ("bb", "Barbados"), + ("bn", "Brunei Darussalam"), + ("bt", "Bhutan"), + ("bv", "Bouvet Island"), + ("bw", "Botswana"), + ("cf", "Central African Republic"), + ("ca", "Canada"), + ("cc", "Cocos (Keeling) Islands"), + ("ch", "Switzerland"), + ("cl", "Chile"), + ("cn", "China"), + ("ci", "Côte d'Ivoire"), + ("cm", "Cameroon"), + ("cd", "Congo, The Democratic Republic of the"), + ("cg", "Congo"), + ("ck", "Cook Islands"), + ("co", "Colombia"), + ("km", "Comoros"), + ("cv", "Cabo Verde"), + ("cr", "Costa Rica"), + ("cu", "Cuba"), + ("cw", "Curaçao"), + ("cx", "Christmas Island"), + ("ky", "Cayman Islands"), + ("cy", "Cyprus"), + ("cz", "Czechia"), + ("de", "Germany"), + ("dj", "Djibouti"), + ("dm", "Dominica"), + ("dk", "Denmark"), + ("do", "Dominican Republic"), + ("dz", "Algeria"), + ("ec", "Ecuador"), + ("eg", "Egypt"), + ("er", "Eritrea"), + ("eh", "Western Sahara"), + ("es", "Spain"), + ("ee", "Estonia"), + ("et", "Ethiopia"), + ("fi", "Finland"), + ("fj", "Fiji"), + ("fk", "Falkland Islands (Malvinas)"), + ("fr", "France"), + ("fo", "Faroe Islands"), + ("fm", "Micronesia, Federated States of"), + ("ga", "Gabon"), + ("gb", "United Kingdom"), + ("ge", "Georgia"), + ("gg", "Guernsey"), + ("gh", "Ghana"), + ("gi", "Gibraltar"), + ("gn", "Guinea"), + ("gp", "Guadeloupe"), + ("gm", "Gambia"), + ("gw", "Guinea-Bissau"), + ("gq", "Equatorial Guinea"), + ("gr", "Greece"), + ("gd", "Grenada"), + ("gl", "Greenland"), + ("gt", "Guatemala"), + ("gf", "French Guiana"), + ("gu", "Guam"), + ("gy", "Guyana"), + ("hk", "Hong Kong"), + ("hm", "Heard Island and McDonald Islands"), + ("hn", "Honduras"), + ("hr", "Croatia"), + ("ht", "Haiti"), + ("hu", "Hungary"), + ("id", "Indonesia"), + ("im", "Isle of Man"), + ("in", "India"), + ("io", "British Indian Ocean Territory"), + ("ie", "Ireland"), + ("ir", "Iran, Islamic Republic of"), + ("iq", "Iraq"), + ("is", "Iceland"), + ("il", "Israel"), + ("it", "Italy"), + ("jm", "Jamaica"), + ("je", "Jersey"), + ("jo", "Jordan"), + ("jp", "Japan"), + ("kz", "Kazakhstan"), + ("ke", "Kenya"), + ("kg", "Kyrgyzstan"), + ("kh", "Cambodia"), + ("ki", "Kiribati"), + ("kn", "Saint Kitts and Nevis"), + ("kr", "Korea, Republic of"), + ("kw", "Kuwait"), + ("la", "Lao People's Democratic Republic"), + ("lb", "Lebanon"), + ("lr", "Liberia"), + ("ly", "Libya"), + ("lc", "Saint Lucia"), + ("li", "Liechtenstein"), + ("lk", "Sri Lanka"), + ("ls", "Lesotho"), + ("lt", "Lithuania"), + ("lu", "Luxembourg"), + ("lv", "Latvia"), + ("mo", "Macao"), + ("mf", "Saint Martin (French part)"), + ("ma", "Morocco"), + ("mc", "Monaco"), + ("md", "Moldova, Republic of"), + ("mg", "Madagascar"), + ("mv", "Maldives"), + ("mx", "Mexico"), + ("mh", "Marshall Islands"), + ("mk", "Macedonia, Republic of"), + ("ml", "Mali"), + ("mt", "Malta"), + ("mm", "Myanmar"), + ("me", "Montenegro"), + ("mn", "Mongolia"), + ("mp", "Northern Mariana Islands"), + ("mz", "Mozambique"), + ("mr", "Mauritania"), + ("ms", "Montserrat"), + ("mq", "Martinique"), + ("mu", "Mauritius"), + ("mw", "Malawi"), + ("my", "Malaysia"), + ("yt", "Mayotte"), + ("na", "Namibia"), + ("nc", "New Caledonia"), + ("ne", "Niger"), + ("nf", "Norfolk Island"), + ("ng", "Nigeria"), + ("ni", "Nicaragua"), + ("nu", "Niue"), + ("nl", "Netherlands"), + ("no", "Norway"), + ("np", "Nepal"), + ("nr", "Nauru"), + ("nz", "New Zealand"), + ("om", "Oman"), + ("pk", "Pakistan"), + ("pa", "Panama"), + ("pn", "Pitcairn"), + ("pe", "Peru"), + ("ph", "Philippines"), + ("pw", "Palau"), + ("pg", "Papua New Guinea"), + ("pl", "Poland"), + ("pr", "Puerto Rico"), + ("kp", "Korea, Democratic People's Republic of"), + ("pt", "Portugal"), + ("py", "Paraguay"), + ("ps", "Palestine, State of"), + ("pf", "French Polynesia"), + ("qa", "Qatar"), + ("re", "Réunion"), + ("ro", "Romania"), + ("ru", "Russian Federation"), + ("rw", "Rwanda"), + ("sa", "Saudi Arabia"), + ("sd", "Sudan"), + ("sn", "Senegal"), + ("sg", "Singapore"), + ("gs", "South Georgia and the South Sandwich Islands"), + ("sh", "Saint Helena, Ascension and Tristan da Cunha"), + ("sj", "Svalbard and Jan Mayen"), + ("sb", "Solomon Islands"), + ("sl", "Sierra Leone"), + ("sv", "El Salvador"), + ("sm", "San Marino"), + ("so", "Somalia"), + ("pm", "Saint Pierre and Miquelon"), + ("rs", "Serbia"), + ("ss", "South Sudan"), + ("st", "Sao Tome and Principe"), + ("sr", "Suriname"), + ("sk", "Slovakia"), + ("si", "Slovenia"), + ("se", "Sweden"), + ("sz", "Swaziland"), + ("sx", "Sint Maarten (Dutch part)"), + ("sc", "Seychelles"), + ("sy", "Syrian Arab Republic"), + ("tc", "Turks and Caicos Islands"), + ("td", "Chad"), + ("tg", "Togo"), + ("th", "Thailand"), + ("tj", "Tajikistan"), + ("tk", "Tokelau"), + ("tm", "Turkmenistan"), + ("tl", "Timor-Leste"), + ("to", "Tonga"), + ("tt", "Trinidad and Tobago"), + ("tn", "Tunisia"), + ("tr", "Turkey"), + ("tv", "Tuvalu"), + ("tw", "Taiwan, Province of China"), + ("tz", "Tanzania, United Republic of"), + ("ug", "Uganda"), + ("ua", "Ukraine"), + ("um", "United States Minor Outlying Islands"), + ("uy", "Uruguay"), + ("us", "United States"), + ("uz", "Uzbekistan"), + ("va", "Holy See (Vatican City State)"), + ("vc", "Saint Vincent and the Grenadines"), + ("ve", "Venezuela, Bolivarian Republic of"), + ("vg", "Virgin Islands, British"), + ("vi", "Virgin Islands, U.S."), + ("vn", "Viet Nam"), + ("vu", "Vanuatu"), + ("wf", "Wallis and Futuna"), + ("ws", "Samoa"), + ("ye", "Yemen"), + ("za", "South Africa"), + ("zm", "Zambia"), + ("zw", "Zimbabwe"), + ], + help_text="Select origin country of the request that this rule will match against. This rule will only work if you use Cloudflare or CloudFront IP geolocation or if GeoIP2 module is configured.", + max_length=2, + ), + ), + ( + "segment", + modelcluster.fields.ParentalKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="wagtail_personalisation_origincountryrules", + to="wagtail_personalisation.Segment", + ), + ), ], options={ - 'verbose_name': 'origin country rule', + "verbose_name": "origin country rule", }, ), ] diff --git a/src/wagtail_personalisation/migrations/0025_auto_20190822_0627.py b/src/wagtail_personalisation/migrations/0025_auto_20190822_0627.py index b2e1b02..2f68fd3 100644 --- a/src/wagtail_personalisation/migrations/0025_auto_20190822_0627.py +++ b/src/wagtail_personalisation/migrations/0025_auto_20190822_0627.py @@ -6,13 +6,267 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('wagtail_personalisation', '0024_origincountryrule'), + ("wagtail_personalisation", "0024_origincountryrule"), ] operations = [ migrations.AlterField( - model_name='origincountryrule', - name='country', - field=models.CharField(choices=[('aw', 'Aruba'), ('af', 'Afghanistan'), ('ao', 'Angola'), ('ai', 'Anguilla'), ('ax', 'Åland Islands'), ('al', 'Albania'), ('ad', 'Andorra'), ('ae', 'United Arab Emirates'), ('ar', 'Argentina'), ('am', 'Armenia'), ('as', 'American Samoa'), ('aq', 'Antarctica'), ('tf', 'French Southern Territories'), ('ag', 'Antigua and Barbuda'), ('au', 'Australia'), ('at', 'Austria'), ('az', 'Azerbaijan'), ('bi', 'Burundi'), ('be', 'Belgium'), ('bj', 'Benin'), ('bq', 'Bonaire, Sint Eustatius and Saba'), ('bf', 'Burkina Faso'), ('bd', 'Bangladesh'), ('bg', 'Bulgaria'), ('bh', 'Bahrain'), ('bs', 'Bahamas'), ('ba', 'Bosnia and Herzegovina'), ('bl', 'Saint Barthélemy'), ('by', 'Belarus'), ('bz', 'Belize'), ('bm', 'Bermuda'), ('bo', 'Bolivia, Plurinational State of'), ('br', 'Brazil'), ('bb', 'Barbados'), ('bn', 'Brunei Darussalam'), ('bt', 'Bhutan'), ('bv', 'Bouvet Island'), ('bw', 'Botswana'), ('cf', 'Central African Republic'), ('ca', 'Canada'), ('cc', 'Cocos (Keeling) Islands'), ('ch', 'Switzerland'), ('cl', 'Chile'), ('cn', 'China'), ('ci', "Côte d'Ivoire"), ('cm', 'Cameroon'), ('cd', 'Congo, The Democratic Republic of the'), ('cg', 'Congo'), ('ck', 'Cook Islands'), ('co', 'Colombia'), ('km', 'Comoros'), ('cv', 'Cabo Verde'), ('cr', 'Costa Rica'), ('cu', 'Cuba'), ('cw', 'Curaçao'), ('cx', 'Christmas Island'), ('ky', 'Cayman Islands'), ('cy', 'Cyprus'), ('cz', 'Czechia'), ('de', 'Germany'), ('dj', 'Djibouti'), ('dm', 'Dominica'), ('dk', 'Denmark'), ('do', 'Dominican Republic'), ('dz', 'Algeria'), ('ec', 'Ecuador'), ('eg', 'Egypt'), ('er', 'Eritrea'), ('eh', 'Western Sahara'), ('es', 'Spain'), ('ee', 'Estonia'), ('et', 'Ethiopia'), ('fi', 'Finland'), ('fj', 'Fiji'), ('fk', 'Falkland Islands (Malvinas)'), ('fr', 'France'), ('fo', 'Faroe Islands'), ('fm', 'Micronesia, Federated States of'), ('ga', 'Gabon'), ('gb', 'United Kingdom'), ('ge', 'Georgia'), ('gg', 'Guernsey'), ('gh', 'Ghana'), ('gi', 'Gibraltar'), ('gn', 'Guinea'), ('gp', 'Guadeloupe'), ('gm', 'Gambia'), ('gw', 'Guinea-Bissau'), ('gq', 'Equatorial Guinea'), ('gr', 'Greece'), ('gd', 'Grenada'), ('gl', 'Greenland'), ('gt', 'Guatemala'), ('gf', 'French Guiana'), ('gu', 'Guam'), ('gy', 'Guyana'), ('hk', 'Hong Kong'), ('hm', 'Heard Island and McDonald Islands'), ('hn', 'Honduras'), ('hr', 'Croatia'), ('ht', 'Haiti'), ('hu', 'Hungary'), ('id', 'Indonesia'), ('im', 'Isle of Man'), ('in', 'India'), ('io', 'British Indian Ocean Territory'), ('ie', 'Ireland'), ('ir', 'Iran, Islamic Republic of'), ('iq', 'Iraq'), ('is', 'Iceland'), ('il', 'Israel'), ('it', 'Italy'), ('jm', 'Jamaica'), ('je', 'Jersey'), ('jo', 'Jordan'), ('jp', 'Japan'), ('kz', 'Kazakhstan'), ('ke', 'Kenya'), ('kg', 'Kyrgyzstan'), ('kh', 'Cambodia'), ('ki', 'Kiribati'), ('kn', 'Saint Kitts and Nevis'), ('kr', 'Korea, Republic of'), ('kw', 'Kuwait'), ('la', "Lao People's Democratic Republic"), ('lb', 'Lebanon'), ('lr', 'Liberia'), ('ly', 'Libya'), ('lc', 'Saint Lucia'), ('li', 'Liechtenstein'), ('lk', 'Sri Lanka'), ('ls', 'Lesotho'), ('lt', 'Lithuania'), ('lu', 'Luxembourg'), ('lv', 'Latvia'), ('mo', 'Macao'), ('mf', 'Saint Martin (French part)'), ('ma', 'Morocco'), ('mc', 'Monaco'), ('md', 'Moldova, Republic of'), ('mg', 'Madagascar'), ('mv', 'Maldives'), ('mx', 'Mexico'), ('mh', 'Marshall Islands'), ('mk', 'North Macedonia'), ('ml', 'Mali'), ('mt', 'Malta'), ('mm', 'Myanmar'), ('me', 'Montenegro'), ('mn', 'Mongolia'), ('mp', 'Northern Mariana Islands'), ('mz', 'Mozambique'), ('mr', 'Mauritania'), ('ms', 'Montserrat'), ('mq', 'Martinique'), ('mu', 'Mauritius'), ('mw', 'Malawi'), ('my', 'Malaysia'), ('yt', 'Mayotte'), ('na', 'Namibia'), ('nc', 'New Caledonia'), ('ne', 'Niger'), ('nf', 'Norfolk Island'), ('ng', 'Nigeria'), ('ni', 'Nicaragua'), ('nu', 'Niue'), ('nl', 'Netherlands'), ('no', 'Norway'), ('np', 'Nepal'), ('nr', 'Nauru'), ('nz', 'New Zealand'), ('om', 'Oman'), ('pk', 'Pakistan'), ('pa', 'Panama'), ('pn', 'Pitcairn'), ('pe', 'Peru'), ('ph', 'Philippines'), ('pw', 'Palau'), ('pg', 'Papua New Guinea'), ('pl', 'Poland'), ('pr', 'Puerto Rico'), ('kp', "Korea, Democratic People's Republic of"), ('pt', 'Portugal'), ('py', 'Paraguay'), ('ps', 'Palestine, State of'), ('pf', 'French Polynesia'), ('qa', 'Qatar'), ('re', 'Réunion'), ('ro', 'Romania'), ('ru', 'Russian Federation'), ('rw', 'Rwanda'), ('sa', 'Saudi Arabia'), ('sd', 'Sudan'), ('sn', 'Senegal'), ('sg', 'Singapore'), ('gs', 'South Georgia and the South Sandwich Islands'), ('sh', 'Saint Helena, Ascension and Tristan da Cunha'), ('sj', 'Svalbard and Jan Mayen'), ('sb', 'Solomon Islands'), ('sl', 'Sierra Leone'), ('sv', 'El Salvador'), ('sm', 'San Marino'), ('so', 'Somalia'), ('pm', 'Saint Pierre and Miquelon'), ('rs', 'Serbia'), ('ss', 'South Sudan'), ('st', 'Sao Tome and Principe'), ('sr', 'Suriname'), ('sk', 'Slovakia'), ('si', 'Slovenia'), ('se', 'Sweden'), ('sz', 'Eswatini'), ('sx', 'Sint Maarten (Dutch part)'), ('sc', 'Seychelles'), ('sy', 'Syrian Arab Republic'), ('tc', 'Turks and Caicos Islands'), ('td', 'Chad'), ('tg', 'Togo'), ('th', 'Thailand'), ('tj', 'Tajikistan'), ('tk', 'Tokelau'), ('tm', 'Turkmenistan'), ('tl', 'Timor-Leste'), ('to', 'Tonga'), ('tt', 'Trinidad and Tobago'), ('tn', 'Tunisia'), ('tr', 'Turkey'), ('tv', 'Tuvalu'), ('tw', 'Taiwan, Province of China'), ('tz', 'Tanzania, United Republic of'), ('ug', 'Uganda'), ('ua', 'Ukraine'), ('um', 'United States Minor Outlying Islands'), ('uy', 'Uruguay'), ('us', 'United States'), ('uz', 'Uzbekistan'), ('va', 'Holy See (Vatican City State)'), ('vc', 'Saint Vincent and the Grenadines'), ('ve', 'Venezuela, Bolivarian Republic of'), ('vg', 'Virgin Islands, British'), ('vi', 'Virgin Islands, U.S.'), ('vn', 'Viet Nam'), ('vu', 'Vanuatu'), ('wf', 'Wallis and Futuna'), ('ws', 'Samoa'), ('ye', 'Yemen'), ('za', 'South Africa'), ('zm', 'Zambia'), ('zw', 'Zimbabwe')], help_text='Select origin country of the request that this rule will match against. This rule will only work if you use Cloudflare or CloudFront IP geolocation or if GeoIP2 module is configured.', max_length=2), + model_name="origincountryrule", + name="country", + field=models.CharField( + choices=[ + ("aw", "Aruba"), + ("af", "Afghanistan"), + ("ao", "Angola"), + ("ai", "Anguilla"), + ("ax", "Åland Islands"), + ("al", "Albania"), + ("ad", "Andorra"), + ("ae", "United Arab Emirates"), + ("ar", "Argentina"), + ("am", "Armenia"), + ("as", "American Samoa"), + ("aq", "Antarctica"), + ("tf", "French Southern Territories"), + ("ag", "Antigua and Barbuda"), + ("au", "Australia"), + ("at", "Austria"), + ("az", "Azerbaijan"), + ("bi", "Burundi"), + ("be", "Belgium"), + ("bj", "Benin"), + ("bq", "Bonaire, Sint Eustatius and Saba"), + ("bf", "Burkina Faso"), + ("bd", "Bangladesh"), + ("bg", "Bulgaria"), + ("bh", "Bahrain"), + ("bs", "Bahamas"), + ("ba", "Bosnia and Herzegovina"), + ("bl", "Saint Barthélemy"), + ("by", "Belarus"), + ("bz", "Belize"), + ("bm", "Bermuda"), + ("bo", "Bolivia, Plurinational State of"), + ("br", "Brazil"), + ("bb", "Barbados"), + ("bn", "Brunei Darussalam"), + ("bt", "Bhutan"), + ("bv", "Bouvet Island"), + ("bw", "Botswana"), + ("cf", "Central African Republic"), + ("ca", "Canada"), + ("cc", "Cocos (Keeling) Islands"), + ("ch", "Switzerland"), + ("cl", "Chile"), + ("cn", "China"), + ("ci", "Côte d'Ivoire"), + ("cm", "Cameroon"), + ("cd", "Congo, The Democratic Republic of the"), + ("cg", "Congo"), + ("ck", "Cook Islands"), + ("co", "Colombia"), + ("km", "Comoros"), + ("cv", "Cabo Verde"), + ("cr", "Costa Rica"), + ("cu", "Cuba"), + ("cw", "Curaçao"), + ("cx", "Christmas Island"), + ("ky", "Cayman Islands"), + ("cy", "Cyprus"), + ("cz", "Czechia"), + ("de", "Germany"), + ("dj", "Djibouti"), + ("dm", "Dominica"), + ("dk", "Denmark"), + ("do", "Dominican Republic"), + ("dz", "Algeria"), + ("ec", "Ecuador"), + ("eg", "Egypt"), + ("er", "Eritrea"), + ("eh", "Western Sahara"), + ("es", "Spain"), + ("ee", "Estonia"), + ("et", "Ethiopia"), + ("fi", "Finland"), + ("fj", "Fiji"), + ("fk", "Falkland Islands (Malvinas)"), + ("fr", "France"), + ("fo", "Faroe Islands"), + ("fm", "Micronesia, Federated States of"), + ("ga", "Gabon"), + ("gb", "United Kingdom"), + ("ge", "Georgia"), + ("gg", "Guernsey"), + ("gh", "Ghana"), + ("gi", "Gibraltar"), + ("gn", "Guinea"), + ("gp", "Guadeloupe"), + ("gm", "Gambia"), + ("gw", "Guinea-Bissau"), + ("gq", "Equatorial Guinea"), + ("gr", "Greece"), + ("gd", "Grenada"), + ("gl", "Greenland"), + ("gt", "Guatemala"), + ("gf", "French Guiana"), + ("gu", "Guam"), + ("gy", "Guyana"), + ("hk", "Hong Kong"), + ("hm", "Heard Island and McDonald Islands"), + ("hn", "Honduras"), + ("hr", "Croatia"), + ("ht", "Haiti"), + ("hu", "Hungary"), + ("id", "Indonesia"), + ("im", "Isle of Man"), + ("in", "India"), + ("io", "British Indian Ocean Territory"), + ("ie", "Ireland"), + ("ir", "Iran, Islamic Republic of"), + ("iq", "Iraq"), + ("is", "Iceland"), + ("il", "Israel"), + ("it", "Italy"), + ("jm", "Jamaica"), + ("je", "Jersey"), + ("jo", "Jordan"), + ("jp", "Japan"), + ("kz", "Kazakhstan"), + ("ke", "Kenya"), + ("kg", "Kyrgyzstan"), + ("kh", "Cambodia"), + ("ki", "Kiribati"), + ("kn", "Saint Kitts and Nevis"), + ("kr", "Korea, Republic of"), + ("kw", "Kuwait"), + ("la", "Lao People's Democratic Republic"), + ("lb", "Lebanon"), + ("lr", "Liberia"), + ("ly", "Libya"), + ("lc", "Saint Lucia"), + ("li", "Liechtenstein"), + ("lk", "Sri Lanka"), + ("ls", "Lesotho"), + ("lt", "Lithuania"), + ("lu", "Luxembourg"), + ("lv", "Latvia"), + ("mo", "Macao"), + ("mf", "Saint Martin (French part)"), + ("ma", "Morocco"), + ("mc", "Monaco"), + ("md", "Moldova, Republic of"), + ("mg", "Madagascar"), + ("mv", "Maldives"), + ("mx", "Mexico"), + ("mh", "Marshall Islands"), + ("mk", "North Macedonia"), + ("ml", "Mali"), + ("mt", "Malta"), + ("mm", "Myanmar"), + ("me", "Montenegro"), + ("mn", "Mongolia"), + ("mp", "Northern Mariana Islands"), + ("mz", "Mozambique"), + ("mr", "Mauritania"), + ("ms", "Montserrat"), + ("mq", "Martinique"), + ("mu", "Mauritius"), + ("mw", "Malawi"), + ("my", "Malaysia"), + ("yt", "Mayotte"), + ("na", "Namibia"), + ("nc", "New Caledonia"), + ("ne", "Niger"), + ("nf", "Norfolk Island"), + ("ng", "Nigeria"), + ("ni", "Nicaragua"), + ("nu", "Niue"), + ("nl", "Netherlands"), + ("no", "Norway"), + ("np", "Nepal"), + ("nr", "Nauru"), + ("nz", "New Zealand"), + ("om", "Oman"), + ("pk", "Pakistan"), + ("pa", "Panama"), + ("pn", "Pitcairn"), + ("pe", "Peru"), + ("ph", "Philippines"), + ("pw", "Palau"), + ("pg", "Papua New Guinea"), + ("pl", "Poland"), + ("pr", "Puerto Rico"), + ("kp", "Korea, Democratic People's Republic of"), + ("pt", "Portugal"), + ("py", "Paraguay"), + ("ps", "Palestine, State of"), + ("pf", "French Polynesia"), + ("qa", "Qatar"), + ("re", "Réunion"), + ("ro", "Romania"), + ("ru", "Russian Federation"), + ("rw", "Rwanda"), + ("sa", "Saudi Arabia"), + ("sd", "Sudan"), + ("sn", "Senegal"), + ("sg", "Singapore"), + ("gs", "South Georgia and the South Sandwich Islands"), + ("sh", "Saint Helena, Ascension and Tristan da Cunha"), + ("sj", "Svalbard and Jan Mayen"), + ("sb", "Solomon Islands"), + ("sl", "Sierra Leone"), + ("sv", "El Salvador"), + ("sm", "San Marino"), + ("so", "Somalia"), + ("pm", "Saint Pierre and Miquelon"), + ("rs", "Serbia"), + ("ss", "South Sudan"), + ("st", "Sao Tome and Principe"), + ("sr", "Suriname"), + ("sk", "Slovakia"), + ("si", "Slovenia"), + ("se", "Sweden"), + ("sz", "Eswatini"), + ("sx", "Sint Maarten (Dutch part)"), + ("sc", "Seychelles"), + ("sy", "Syrian Arab Republic"), + ("tc", "Turks and Caicos Islands"), + ("td", "Chad"), + ("tg", "Togo"), + ("th", "Thailand"), + ("tj", "Tajikistan"), + ("tk", "Tokelau"), + ("tm", "Turkmenistan"), + ("tl", "Timor-Leste"), + ("to", "Tonga"), + ("tt", "Trinidad and Tobago"), + ("tn", "Tunisia"), + ("tr", "Turkey"), + ("tv", "Tuvalu"), + ("tw", "Taiwan, Province of China"), + ("tz", "Tanzania, United Republic of"), + ("ug", "Uganda"), + ("ua", "Ukraine"), + ("um", "United States Minor Outlying Islands"), + ("uy", "Uruguay"), + ("us", "United States"), + ("uz", "Uzbekistan"), + ("va", "Holy See (Vatican City State)"), + ("vc", "Saint Vincent and the Grenadines"), + ("ve", "Venezuela, Bolivarian Republic of"), + ("vg", "Virgin Islands, British"), + ("vi", "Virgin Islands, U.S."), + ("vn", "Viet Nam"), + ("vu", "Vanuatu"), + ("wf", "Wallis and Futuna"), + ("ws", "Samoa"), + ("ye", "Yemen"), + ("za", "South Africa"), + ("zm", "Zambia"), + ("zw", "Zimbabwe"), + ], + help_text="Select origin country of the request that this rule will match against. This rule will only work if you use Cloudflare or CloudFront IP geolocation or if GeoIP2 module is configured.", + max_length=2, + ), ), ] diff --git a/src/wagtail_personalisation/models.py b/src/wagtail_personalisation/models.py index 1b28dd4..b2f1dcb 100644 --- a/src/wagtail_personalisation/models.py +++ b/src/wagtail_personalisation/models.py @@ -11,7 +11,11 @@ from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _ from modelcluster.models import ClusterableModel from wagtail.admin.edit_handlers import ( - FieldPanel, FieldRowPanel, InlinePanel, MultiFieldPanel) + FieldPanel, + FieldRowPanel, + InlinePanel, + MultiFieldPanel, +) from wagtail.core.models import Page from wagtail_personalisation.rules import AbstractBaseRule @@ -22,7 +26,7 @@ from .forms import SegmentAdminForm class RulePanel(InlinePanel): def on_model_bound(self): - self.relation_name = self.relation_name.replace('_related', 's') + 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 @@ -35,20 +39,21 @@ class SegmentQuerySet(models.QuerySet): class Segment(ClusterableModel): """The segment model.""" - STATUS_ENABLED = 'enabled' - STATUS_DISABLED = 'disabled' + + STATUS_ENABLED = "enabled" + STATUS_DISABLED = "disabled" STATUS_CHOICES = ( - (STATUS_ENABLED, _('Enabled')), - (STATUS_DISABLED, _('Disabled')), + (STATUS_ENABLED, _("Enabled")), + (STATUS_DISABLED, _("Disabled")), ) - TYPE_DYNAMIC = 'dynamic' - TYPE_STATIC = 'static' + TYPE_DYNAMIC = "dynamic" + TYPE_STATIC = "static" TYPE_CHOICES = ( - (TYPE_DYNAMIC, _('Dynamic')), - (TYPE_STATIC, _('Static')), + (TYPE_DYNAMIC, _("Dynamic")), + (TYPE_STATIC, _("Static")), ) name = models.CharField(max_length=255) @@ -58,18 +63,22 @@ class Segment(ClusterableModel): disable_date = models.DateTimeField(null=True, editable=False) visit_count = models.PositiveIntegerField(default=0, editable=False) status = models.CharField( - max_length=20, choices=STATUS_CHOICES, default=STATUS_ENABLED) + max_length=20, choices=STATUS_CHOICES, default=STATUS_ENABLED + ) persistent = models.BooleanField( - default=False, help_text=_("Should the segment persist between visits?")) + 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?") + help_text=_("Should the segment match all the rules or just one of them?"), ) type = models.CharField( max_length=20, choices=TYPE_CHOICES, default=TYPE_DYNAMIC, - help_text=mark_safe(_(""" + help_text=mark_safe( + _( + """

Dynamic: Users in this segment will change as more or less meet the rules specified in the segment.
Static: If the segment contains only static @@ -77,37 +86,42 @@ class Segment(ClusterableModel): those rules when the segment is created. Mixed static segments or those containing entirely non static compatible rules will be populated using the count variable. - """)) + """ + ) + ), ) count = models.PositiveSmallIntegerField( default=0, help_text=_( "If this number is set for a static segment users will be added to the " "set until the number is reached. After this no more users will be added." - ) + ), ) static_users = models.ManyToManyField( settings.AUTH_USER_MODEL, ) excluded_users = models.ManyToManyField( settings.AUTH_USER_MODEL, - help_text=_("Users that matched the rules but were excluded from the " - "segment for some reason e.g. randomisation"), - related_name="excluded_segments" + help_text=_( + "Users that matched the rules but were excluded from the " + "segment for some reason e.g. randomisation" + ), + related_name="excluded_segments", ) matched_users_count = models.PositiveIntegerField(default=0, editable=False) matched_count_updated_at = models.DateTimeField(null=True, editable=False) randomisation_percent = models.PositiveSmallIntegerField( - null=True, blank=True, default=None, + null=True, + blank=True, + default=None, help_text=_( "If this number is set each user matching the rules will " "have this percentage chance of being placed in the segment." - ), validators=[ - MaxValueValidator(100), - MinValueValidator(0) - ]) + ), + validators=[MaxValueValidator(100), MinValueValidator(0)], + ) objects = SegmentQuerySet.as_manager() @@ -115,26 +129,37 @@ class Segment(ClusterableModel): def __init__(self, *args, **kwargs): Segment.panels = [ - MultiFieldPanel([ - FieldPanel('name', classname="title"), - FieldRowPanel([ - FieldPanel('status'), - FieldPanel('persistent'), - ]), - FieldPanel('match_any'), - FieldPanel('type', widget=forms.RadioSelect), - FieldPanel('count', classname='count_field'), - FieldPanel('randomisation_percent', classname='percent_field'), - ], heading="Segment"), - MultiFieldPanel([ - RulePanel( - "{}_related".format(rule_model._meta.db_table), - label='{}{}'.format( - rule_model._meta.verbose_name, - ' ({})'.format(_('Static compatible')) if rule_model.static else '' + MultiFieldPanel( + [ + FieldPanel("name", classname="title"), + FieldRowPanel( + [ + FieldPanel("status"), + FieldPanel("persistent"), + ] ), - ) for rule_model in AbstractBaseRule.__subclasses__() - ], heading=_("Rules")), + FieldPanel("match_any"), + FieldPanel("type", widget=forms.RadioSelect), + FieldPanel("count", classname="count_field"), + FieldPanel("randomisation_percent", classname="percent_field"), + ], + heading="Segment", + ), + MultiFieldPanel( + [ + RulePanel( + "{}_related".format(rule_model._meta.db_table), + label="{}{}".format( + rule_model._meta.verbose_name, + " ({})".format(_("Static compatible")) + if rule_model.static + else "", + ), + ) + for rule_model in AbstractBaseRule.__subclasses__() + ], + heading=_("Rules"), + ), ] super(Segment, self).__init__(*args, **kwargs) @@ -179,20 +204,21 @@ class Segment(ClusterableModel): """Retrieve all rules in the segment.""" segment_rules = [] for rule_model in AbstractBaseRule.get_descendant_models(): - segment_rules.extend( - rule_model._default_manager.filter(segment=self)) + segment_rules.extend(rule_model._default_manager.filter(segment=self)) return segment_rules def toggle(self, save=True): self.status = ( - self.STATUS_ENABLED if self.status == self.STATUS_DISABLED - else self.STATUS_DISABLED) + self.STATUS_ENABLED + if self.status == self.STATUS_DISABLED + else self.STATUS_DISABLED + ) if save: self.save() def randomise_into_segment(self): - """ Returns True if randomisation_percent is not set or it generates + """Returns True if randomisation_percent is not set or it generates a random number less than the randomisation_percent This is so there is some randomisation in which users are added to the segment @@ -210,21 +236,24 @@ class PersonalisablePageMetadata(ClusterableModel): segments. """ + # Canonical pages should not ever be deleted if they have variants # because the variants will be orphaned. canonical_page = models.ForeignKey( - Page, models.PROTECT, related_name='personalisable_canonical_metadata', - null=True + Page, + models.PROTECT, + related_name="personalisable_canonical_metadata", + null=True, ) # Delete metadata of the variant if its page gets deleted. variant = models.OneToOneField( - Page, models.CASCADE, related_name='_personalisable_page_metadata', - null=True + Page, models.CASCADE, related_name="_personalisable_page_metadata", null=True ) - segment = models.ForeignKey(Segment, models.PROTECT, null=True, - related_name='page_metadata') + segment = models.ForeignKey( + Segment, models.PROTECT, null=True, related_name="page_metadata" + ) @cached_property def has_variants(self): @@ -241,10 +270,12 @@ class PersonalisablePageMetadata(ClusterableModel): @cached_property def variants_metadata(self): return ( - PersonalisablePageMetadata.objects - .filter(canonical_page_id=self.canonical_page_id) + PersonalisablePageMetadata.objects.filter( + canonical_page_id=self.canonical_page_id + ) .exclude(variant_id=self.variant_id) - .exclude(variant_id=self.canonical_page_id)) + .exclude(variant_id=self.canonical_page_id) + ) @cached_property def is_canonical(self): @@ -265,33 +296,31 @@ class PersonalisablePageMetadata(ClusterableModel): slug = "{}-{}".format(page.slug, segment.encoded_name()) title = "{} ({})".format(page.title, segment.name) update_attrs = { - 'title': title, - 'slug': slug, - 'live': False, + "title": title, + "slug": slug, + "live": False, } with transaction.atomic(): new_page = self.canonical_page.copy( - update_attrs=update_attrs, copy_revisions=False) + update_attrs=update_attrs, copy_revisions=False + ) PersonalisablePageMetadata.objects.create( - canonical_page=page, - variant=new_page, - segment=segment) + canonical_page=page, variant=new_page, segment=segment + ) return new_page def metadata_for_segments(self, segments): - return ( - self.__class__.objects - .filter( - canonical_page_id=self.canonical_page_id, - segment__in=segments)) + return self.__class__.objects.filter( + canonical_page_id=self.canonical_page_id, segment__in=segments + ) def get_unused_segments(self): if self.is_canonical: - return ( - Segment.objects - .exclude(page_metadata__canonical_page_id=self.canonical_page_id)) + return Segment.objects.exclude( + page_metadata__canonical_page_id=self.canonical_page_id + ) return Segment.objects.none() @@ -307,7 +336,8 @@ class PersonalisablePageMixin: metadata = self._personalisable_page_metadata except AttributeError: metadata = PersonalisablePageMetadata.objects.create( - canonical_page=self, variant=self) + canonical_page=self, variant=self + ) return metadata def get_sitemap_urls(self, request=None): diff --git a/src/wagtail_personalisation/rules.py b/src/wagtail_personalisation/rules.py index 5aee4b8..a06f623 100644 --- a/src/wagtail_personalisation/rules.py +++ b/src/wagtail_personalisation/rules.py @@ -14,8 +14,7 @@ from django.utils import timezone from django.utils.translation import ugettext_lazy as _ from modelcluster.fields import ParentalKey from user_agents import parse -from wagtail.admin.edit_handlers import ( - FieldPanel, FieldRowPanel, PageChooserPanel) +from wagtail.admin.edit_handlers import FieldPanel, FieldRowPanel, PageChooserPanel from wagtail_personalisation.utils import get_client_ip @@ -27,32 +26,35 @@ logger = logging.getLogger(__name__) def get_geoip_module(): try: from django.contrib.gis.geoip2 import GeoIP2 + return GeoIP2 except ImportError: logger.exception( - 'GeoIP module is disabled. To use GeoIP for the origin\n' - 'country personaliastion rule please set it up as per ' - 'documentation:\n' - 'https://docs.djangoproject.com/en/stable/ref/contrib/gis/' - 'geoip2/.\n' - 'Wagtail-personalisation also works with Cloudflare and\n' - 'CloudFront country detection, so you should not see this\n' - 'warning if you use one of those.') + "GeoIP module is disabled. To use GeoIP for the origin\n" + "country personaliastion rule please set it up as per " + "documentation:\n" + "https://docs.djangoproject.com/en/stable/ref/contrib/gis/" + "geoip2/.\n" + "Wagtail-personalisation also works with Cloudflare and\n" + "CloudFront country detection, so you should not see this\n" + "warning if you use one of those." + ) class AbstractBaseRule(models.Model): """Base for creating rules to segment users with.""" - icon = 'fa-circle-o' + + icon = "fa-circle-o" static = False segment = ParentalKey( - 'wagtail_personalisation.Segment', + "wagtail_personalisation.Segment", related_name="%(app_label)s_%(class)ss", ) class Meta: abstract = True - verbose_name = 'Abstract segmentation rule' + verbose_name = "Abstract segmentation rule" def __str__(self): return str(self._meta.verbose_name) @@ -74,16 +76,17 @@ class AbstractBaseRule(models.Model): """ description = { - 'title': _('Abstract segmentation rule'), - 'value': '', + "title": _("Abstract segmentation rule"), + "value": "", } return description @classmethod def get_descendant_models(cls): - return [model for model in apps.get_models() - if issubclass(model, AbstractBaseRule)] + return [ + model for model in apps.get_models() if issubclass(model, AbstractBaseRule) + ] class TimeRule(AbstractBaseRule): @@ -93,30 +96,32 @@ class TimeRule(AbstractBaseRule): set start time and end time. """ - icon = 'fa-clock-o' + + icon = "fa-clock-o" start_time = models.TimeField(_("Starting time")) end_time = models.TimeField(_("Ending time")) panels = [ - FieldRowPanel([ - FieldPanel('start_time'), - FieldPanel('end_time'), - ]), + FieldRowPanel( + [ + FieldPanel("start_time"), + FieldPanel("end_time"), + ] + ), ] class Meta: - verbose_name = _('Time Rule') + verbose_name = _("Time Rule") def test_user(self, request=None): return self.start_time <= timezone.now().time() <= self.end_time def description(self): return { - 'title': _('These users visit between'), - 'value': _('{} and {}').format( - self.start_time.strftime("%H:%M"), - self.end_time.strftime("%H:%M") + "title": _("These users visit between"), + "value": _("{} and {}").format( + self.start_time.strftime("%H:%M"), self.end_time.strftime("%H:%M") ), } @@ -128,7 +133,8 @@ class DayRule(AbstractBaseRule): set in the rule. """ - icon = 'fa-calendar-check-o' + + icon = "fa-calendar-check-o" mon = models.BooleanField(_("Monday"), default=False) tue = models.BooleanField(_("Tuesday"), default=False) @@ -139,34 +145,39 @@ class DayRule(AbstractBaseRule): sun = models.BooleanField(_("Sunday"), default=False) panels = [ - FieldPanel('mon'), - FieldPanel('tue'), - FieldPanel('wed'), - FieldPanel('thu'), - FieldPanel('fri'), - FieldPanel('sat'), - FieldPanel('sun'), + FieldPanel("mon"), + FieldPanel("tue"), + FieldPanel("wed"), + FieldPanel("thu"), + FieldPanel("fri"), + FieldPanel("sat"), + FieldPanel("sun"), ] class Meta: - verbose_name = _('Day Rule') + verbose_name = _("Day Rule") 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()] + return [self.mon, self.tue, self.wed, self.thu, self.fri, self.sat, self.sun][ + timezone.now().date().weekday() + ] def description(self): days = ( - ('mon', self.mon), ('tue', self.tue), ('wed', self.wed), - ('thu', self.thu), ('fri', self.fri), ('sat', self.sat), - ('sun', self.sun), + ("mon", self.mon), + ("tue", self.tue), + ("wed", self.wed), + ("thu", self.thu), + ("fri", self.fri), + ("sat", self.sat), + ("sun", self.sun), ) chosen_days = [day_name for day_name, chosen in days if chosen] return { - 'title': _('These users visit on'), - 'value': ", ".join([day for day in chosen_days]).title(), + "title": _("These users visit on"), + "value": ", ".join([day for day in chosen_days]).title(), } @@ -177,32 +188,32 @@ class ReferralRule(AbstractBaseRule): the set regex test. """ - icon = 'fa-globe' - regex_string = models.TextField( - _("Regular expression to match the referrer")) + icon = "fa-globe" + + regex_string = models.TextField(_("Regular expression to match the referrer")) panels = [ - FieldPanel('regex_string'), + FieldPanel("regex_string"), ] class Meta: - verbose_name = _('Referral Rule') + verbose_name = _("Referral Rule") def test_user(self, request): pattern = re.compile(self.regex_string) - if 'HTTP_REFERER' in request.META: - referer = request.META['HTTP_REFERER'] + if "HTTP_REFERER" in request.META: + referer = request.META["HTTP_REFERER"] if pattern.search(referer): return True return False def description(self): return { - 'title': _('These visits originate from'), - 'value': self.regex_string, - 'code': True + "title": _("These visits originate from"), + "value": self.regex_string, + "code": True, } @@ -214,48 +225,55 @@ class VisitCountRule(AbstractBaseRule): when visiting the set page. """ - icon = 'fa-calculator' + + icon = "fa-calculator" static = True OPERATOR_CHOICES = ( - ('more_than', _("More than")), - ('less_than', _("Less than")), - ('equal_to', _("Equal to")), + ("more_than", _("More than")), + ("less_than", _("Less than")), + ("equal_to", _("Equal to")), + ) + operator = models.CharField( + max_length=20, choices=OPERATOR_CHOICES, default="more_than" ) - operator = models.CharField(max_length=20, - choices=OPERATOR_CHOICES, default="more_than") count = models.PositiveSmallIntegerField(default=0, null=True) counted_page = models.ForeignKey( - 'wagtailcore.Page', + "wagtailcore.Page", null=False, blank=False, on_delete=models.CASCADE, - related_name='+', + related_name="+", ) panels = [ - PageChooserPanel('counted_page'), - FieldRowPanel([ - FieldPanel('operator'), - FieldPanel('count'), - ]), + PageChooserPanel("counted_page"), + FieldRowPanel( + [ + FieldPanel("operator"), + FieldPanel("count"), + ] + ), ] class Meta: - verbose_name = _('Visit count Rule') + verbose_name = _("Visit count Rule") def _get_user_session(self, user): sessions = Session.objects.iterator() for session in sessions: session_data = session.get_decoded() - if session_data.get('_auth_user_id') == str(user.id): + if session_data.get("_auth_user_id") == str(user.id): return SessionStore(session_key=session.session_key) return SessionStore() def test_user(self, request, user=None): # Local import for cyclic import from wagtail_personalisation.adapters import ( - SEGMENT_ADAPTER_CLASS, SessionSegmentsAdapter, get_segment_adapter) + SEGMENT_ADAPTER_CLASS, + SessionSegmentsAdapter, + get_segment_adapter, + ) # Django formsets don't honour 'required' fields so check rule is valid try: @@ -265,7 +283,7 @@ class VisitCountRule(AbstractBaseRule): if user: # Create a fake request so we can use the adapter - request = RequestFactory().get('/') + request = RequestFactory().get("/") request.user = user # If we're using the session adapter check for an active session @@ -297,13 +315,8 @@ class VisitCountRule(AbstractBaseRule): def description(self): return { - 'title': _('These users visited {}').format( - self.counted_page - ), - 'value': _('{} {} times').format( - self.get_operator_display(), - self.count - ), + "title": _("These users visited {}").format(self.counted_page), + "value": _("{} {} times").format(self.get_operator_display(), self.count), } def get_column_header(self): @@ -312,10 +325,13 @@ class VisitCountRule(AbstractBaseRule): def get_user_info_string(self, user): # Local import for cyclic import from wagtail_personalisation.adapters import ( - SEGMENT_ADAPTER_CLASS, SessionSegmentsAdapter, get_segment_adapter) + SEGMENT_ADAPTER_CLASS, + SessionSegmentsAdapter, + get_segment_adapter, + ) # Create a fake request so we can use the adapter - request = RequestFactory().get('/') + request = RequestFactory().get("/") request.user = user # If we're using the session adapter check for an active session @@ -336,32 +352,28 @@ class QueryRule(AbstractBaseRule): present in the request query. """ - icon = 'fa-link' - parameter = models.SlugField(_("The query parameter to search for"), - max_length=20) - value = models.SlugField(_("The value of the parameter to match"), - max_length=20) + icon = "fa-link" + + parameter = models.SlugField(_("The query parameter to search for"), max_length=20) + value = models.SlugField(_("The value of the parameter to match"), max_length=20) panels = [ - FieldPanel('parameter'), - FieldPanel('value'), + FieldPanel("parameter"), + FieldPanel("value"), ] class Meta: - verbose_name = _('Query Rule') + verbose_name = _("Query Rule") def test_user(self, request): - return request.GET.get(self.parameter, '') == self.value + return request.GET.get(self.parameter, "") == self.value def description(self): return { - 'title': _('These users used a URL with the query'), - 'value': _('?{}={}').format( - self.parameter, - self.value - ), - 'code': True + "title": _("These users used a URL with the query"), + "value": _("?{}={}").format(self.parameter, self.value), + "code": True, } @@ -372,23 +384,24 @@ class DeviceRule(AbstractBaseRule): in the request user agent headers. """ - icon = 'fa-tablet' + + icon = "fa-tablet" mobile = models.BooleanField(_("Mobile phone"), default=False) tablet = models.BooleanField(_("Tablet"), default=False) desktop = models.BooleanField(_("Desktop"), default=False) panels = [ - FieldPanel('mobile'), - FieldPanel('tablet'), - FieldPanel('desktop'), + FieldPanel("mobile"), + FieldPanel("tablet"), + FieldPanel("desktop"), ] class Meta: - verbose_name = _('Device Rule') + verbose_name = _("Device Rule") def test_user(self, request=None): - ua_header = request.META['HTTP_USER_AGENT'] + ua_header = request.META["HTTP_USER_AGENT"] user_agent = parse(ua_header) if user_agent.is_mobile: @@ -407,29 +420,31 @@ class UserIsLoggedInRule(AbstractBaseRule): Matches when the user is authenticated. """ - icon = 'fa-user' + + icon = "fa-user" is_logged_in = models.BooleanField(default=False) panels = [ - FieldPanel('is_logged_in'), + FieldPanel("is_logged_in"), ] class Meta: - verbose_name = _('Logged in Rule') + verbose_name = _("Logged in Rule") def test_user(self, request=None): return request.user.is_authenticated == self.is_logged_in def description(self): return { - 'title': _('These visitors are'), - 'value': _('Logged in') if self.is_logged_in else _('Not logged in'), + "title": _("These visitors are"), + "value": _("Logged in") if self.is_logged_in else _("Not logged in"), } -COUNTRY_CHOICES = [(country.alpha_2.lower(), country.name) - for country in pycountry.countries] +COUNTRY_CHOICES = [ + (country.alpha_2.lower(), country.name) for country in pycountry.countries +] class OriginCountryRule(AbstractBaseRule): @@ -439,12 +454,16 @@ class OriginCountryRule(AbstractBaseRule): Using this rule requires setting up GeoIP2 on Django or using CloudFlare or CloudFront geolocation detection. """ + country = models.CharField( - max_length=2, choices=COUNTRY_CHOICES, - help_text=_("Select origin country of the request that this rule will " - "match against. This rule will only work if you use " - "Cloudflare or CloudFront IP geolocation or if GeoIP2 " - "module is configured.") + max_length=2, + choices=COUNTRY_CHOICES, + help_text=_( + "Select origin country of the request that this rule will " + "match against. This rule will only work if you use " + "Cloudflare or CloudFront IP geolocation or if GeoIP2 " + "module is configured." + ), ) class Meta: @@ -458,13 +477,13 @@ class OriginCountryRule(AbstractBaseRule): https://support.cloudflare.com/hc/en-us/articles/200168236-What-does-Cloudflare-IP-Geolocation-do- """ try: - return request.META['HTTP_CF_IPCOUNTRY'].lower() + return request.META["HTTP_CF_IPCOUNTRY"].lower() except KeyError: pass def get_cloudfront_country(self, request): try: - return request.META['HTTP_CLOUDFRONT_VIEWER_COUNTRY'].lower() + return request.META["HTTP_CLOUDFRONT_VIEWER_COUNTRY"].lower() except KeyError: pass @@ -487,4 +506,4 @@ class OriginCountryRule(AbstractBaseRule): return result def test_user(self, request=None): - return (self.get_country(request) or '') == self.country.lower() + return (self.get_country(request) or "") == self.country.lower() diff --git a/src/wagtail_personalisation/templatetags/wagtail_personalisation_filters.py b/src/wagtail_personalisation/templatetags/wagtail_personalisation_filters.py index d408aea..b85d5b1 100644 --- a/src/wagtail_personalisation/templatetags/wagtail_personalisation_filters.py +++ b/src/wagtail_personalisation/templatetags/wagtail_personalisation_filters.py @@ -5,6 +5,6 @@ from wagtail_personalisation.utils import count_active_days register = Library() -@register.filter(name='days_since') +@register.filter(name="days_since") def active_days(enable_date, disable_date): return count_active_days(enable_date, disable_date) diff --git a/src/wagtail_personalisation/templatetags/wagtail_personalisation_tags.py b/src/wagtail_personalisation/templatetags/wagtail_personalisation_tags.py index 6238789..eb08116 100644 --- a/src/wagtail_personalisation/templatetags/wagtail_personalisation_tags.py +++ b/src/wagtail_personalisation/templatetags/wagtail_personalisation_tags.py @@ -15,17 +15,17 @@ def do_segment(parser, token): tag_name, _, kwargs = parse_tag(token, parser) # If no segment is provided this block will raise an error - if set(kwargs.keys()) != {'name'}: + if set(kwargs.keys()) != {"name"}: usage = '{% segment name="segmentname" %} ... {% endsegment %}' raise TemplateSyntaxError("Usage: %s" % usage) - nodelist = parser.parse(('endsegment',)) + nodelist = parser.parse(("endsegment",)) parser.delete_first_token() - return SegmentNode(nodelist, name=kwargs['name']) + return SegmentNode(nodelist, name=kwargs["name"]) -register.tag('segment', do_segment) +register.tag("segment", do_segment) class SegmentNode(template.Node): @@ -36,6 +36,7 @@ class SegmentNode(template.Node): If not it will return nothing """ + def __init__(self, nodelist, name): self.nodelist = nodelist self.name = name @@ -48,10 +49,10 @@ class SegmentNode(template.Node): return "" # Check if user has segment - adapter = get_segment_adapter(context['request']) + adapter = get_segment_adapter(context["request"]) user_segment = adapter.get_segment_by_id(segment_id=segment.pk) if not user_segment: - return '' + return "" content = self.nodelist.render(context) content = mark_safe(content) diff --git a/src/wagtail_personalisation/utils.py b/src/wagtail_personalisation/utils.py index d818182..35dcbc0 100644 --- a/src/wagtail_personalisation/utils.py +++ b/src/wagtail_personalisation/utils.py @@ -36,7 +36,7 @@ def create_segment_dictionary(segment): "encoded_name": segment.encoded_name(), "id": segment.pk, "timestamp": int(time.time()), - "persistent": segment.persistent + "persistent": segment.persistent, } @@ -107,9 +107,10 @@ def exclude_variants(pages): :rtype: QuerySet """ from wagtail_personalisation.models import PersonalisablePageMetadata + excluded_variant_pages = PersonalisablePageMetadata.objects.exclude( - canonical_page_id=F('variant_id') - ).values_list('variant_id') + canonical_page_id=F("variant_id") + ).values_list("variant_id") return pages.exclude(pk__in=excluded_variant_pages) @@ -128,7 +129,7 @@ def get_client_ip(request): else: return func(request) try: - x_forwarded_for = request.META['HTTP_X_FORWARDED_FOR'] - return x_forwarded_for.split(',')[-1].strip() + x_forwarded_for = request.META["HTTP_X_FORWARDED_FOR"] + return x_forwarded_for.split(",")[-1].strip() except KeyError: - return request.META['REMOTE_ADDR'] + return request.META["REMOTE_ADDR"] diff --git a/src/wagtail_personalisation/views.py b/src/wagtail_personalisation/views.py index 0765230..6c98d01 100644 --- a/src/wagtail_personalisation/views.py +++ b/src/wagtail_personalisation/views.py @@ -3,8 +3,7 @@ import csv from django import forms from django.core.exceptions import PermissionDenied from django.db import transaction -from django.http import ( - HttpResponse, HttpResponseForbidden, HttpResponseRedirect) +from django.http import HttpResponse, HttpResponseForbidden, HttpResponseRedirect from django.shortcuts import get_object_or_404 from django.urls import reverse from django.utils.translation import ugettext_lazy as _ @@ -18,41 +17,42 @@ from wagtail_personalisation.utils import can_delete_pages class SegmentModelIndexView(IndexView): """Placeholder for additional list functionality.""" + pass class SegmentModelDashboardView(IndexView): """Additional dashboard functionality.""" + def media(self): return forms.Media( - css={'all': ['css/dashboard.css']}, - js=['js/commons.js', 'js/dashboard.js'] + css={"all": ["css/dashboard.css"]}, js=["js/commons.js", "js/dashboard.js"] ) def get_template_names(self): return [ - 'modeladmin/wagtail_personalisation/segment/dashboard.html', - 'modeladmin/index.html' + "modeladmin/wagtail_personalisation/segment/dashboard.html", + "modeladmin/index.html", ] class SegmentModelDeleteView(DeleteView): def get_affected_page_objects(self): - return Page.objects.filter(pk__in=( - self.instance.get_used_pages().values_list('variant_id', flat=True) - )) + return Page.objects.filter( + pk__in=(self.instance.get_used_pages().values_list("variant_id", flat=True)) + ) def get_template_names(self): return [ - 'modeladmin/wagtail_personalisation/segment/delete.html', - 'modeladmin/delete.html', + "modeladmin/wagtail_personalisation/segment/delete.html", + "modeladmin/delete.html", ] def delete_instance(self): page_variants = self.get_affected_page_objects() if not can_delete_pages(page_variants, self.request.user): raise PermissionDenied( - 'User has no permission to delete variant page objects.' + "User has no permission to delete variant page objects." ) # Deleting page objects triggers deletion of the personalisation # metadata too because of models.CASCADE. @@ -63,8 +63,7 @@ class SegmentModelDeleteView(DeleteView): super().delete_instance() def post(self, request, *args, **kwargs): - if not can_delete_pages(self.get_affected_page_objects(), - self.request.user): + if not can_delete_pages(self.get_affected_page_objects(), self.request.user): context = self.get_context_data( cannot_delete_page_variants_error=True, ) @@ -75,28 +74,39 @@ class SegmentModelDeleteView(DeleteView): @modeladmin_register class SegmentModelAdmin(ModelAdmin): """The model admin for the Segments administration interface.""" + model = Segment index_view_class = SegmentModelIndexView dashboard_view_class = SegmentModelDashboardView delete_view_class = SegmentModelDeleteView - menu_icon = 'fa-snowflake-o' + menu_icon = "fa-snowflake-o" add_to_settings_menu = False - list_display = ('name', 'persistent', 'match_any', 'status', - '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_css = ['css/form.css'] + list_display = ( + "name", + "persistent", + "match_any", + "status", + "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_css = ["css/form.css"] def index_view(self, request): - kwargs = {'model_admin': self} + kwargs = {"model_admin": self} view_class = self.dashboard_view_class - request.session.setdefault('segment_view', 'dashboard') - if request.session['segment_view'] != 'dashboard': + request.session.setdefault("segment_view", "dashboard") + if request.session["segment_view"] != "dashboard": view_class = self.index_view_class return view_class.as_view(**kwargs)(request) @@ -109,7 +119,8 @@ class SegmentModelAdmin(ModelAdmin): def statistics(self, obj): return _("{visits} visits in {days} days").format( - visits=obj.visit_count, days=obj.get_active_days()) + visits=obj.visit_count, days=obj.get_active_days() + ) def toggle_segment_view(request): @@ -121,14 +132,14 @@ def toggle_segment_view(request): :rtype: django.http.HttpResponseRedirect """ - if request.user.has_perm('wagtailadmin.access_admin'): - if request.session['segment_view'] == 'dashboard': - request.session['segment_view'] = 'list' + if request.user.has_perm("wagtailadmin.access_admin"): + if request.session["segment_view"] == "dashboard": + request.session["segment_view"] = "list" - elif request.session['segment_view'] != 'dashboard': - request.session['segment_view'] = 'dashboard' + elif request.session["segment_view"] != "dashboard": + request.session["segment_view"] = "dashboard" - return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) + return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/")) return HttpResponseForbidden() @@ -144,12 +155,12 @@ def toggle(request, segment_id): :rtype: django.http.HttpResponseRedirect """ - if request.user.has_perm('wagtailadmin.access_admin'): + if request.user.has_perm("wagtailadmin.access_admin"): segment = get_object_or_404(Segment, pk=segment_id) segment.toggle() - return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/')) + return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/")) return HttpResponseForbidden() @@ -168,7 +179,7 @@ def copy_page_view(request, page_id, segment_id): :rtype: django.http.HttpResponseRedirect """ - if request.user.has_perm('wagtailadmin.access_admin'): + if request.user.has_perm("wagtailadmin.access_admin"): segment = get_object_or_404(Segment, pk=segment_id) page = get_object_or_404(Page, pk=page_id).specific @@ -178,7 +189,7 @@ def copy_page_view(request, page_id, segment_id): variant = variant_metadata.first() else: variant = metadata.copy_for_segment(segment) - edit_url = reverse('wagtailadmin_pages:edit', args=[variant.id]) + edit_url = reverse("wagtailadmin_pages:edit", args=[variant.id]) return HttpResponseRedirect(edit_url) @@ -187,14 +198,15 @@ def copy_page_view(request, page_id, segment_id): # CSV download views def segment_user_data(request, segment_id): - if request.user.has_perm('wagtailadmin.access_admin'): + if request.user.has_perm("wagtailadmin.access_admin"): segment = get_object_or_404(Segment, pk=segment_id) - response = HttpResponse(content_type='text/csv; charset=utf-8') - response['Content-Disposition'] = \ - 'attachment;filename=segment-%s-users.csv' % str(segment_id) + response = HttpResponse(content_type="text/csv; charset=utf-8") + response[ + "Content-Disposition" + ] = "attachment;filename=segment-%s-users.csv" % str(segment_id) - headers = ['Username'] + headers = ["Username"] for rule in segment.get_rules(): if rule.static: headers.append(rule.get_column_header()) diff --git a/src/wagtail_personalisation/wagtail_hooks.py b/src/wagtail_personalisation/wagtail_hooks.py index aae5639..665d246 100644 --- a/src/wagtail_personalisation/wagtail_hooks.py +++ b/src/wagtail_personalisation/wagtail_hooks.py @@ -211,11 +211,14 @@ class CorrectedPagesSummaryItem(PagesSummaryItem): return page_count if WAGTAIL_VERSION >= (2, 15): + def get_context_data(self, parent_context): context = super().get_context_data(parent_context) context["total_pages"] = self.get_total_pages(context) return context + else: + def get_context(self): context = super().get_context() context["total_pages"] = self.get_total_pages(context) diff --git a/tests/conftest.py b/tests/conftest.py index e677aeb..14a3352 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,8 +1,6 @@ import pytest -pytest_plugins = [ - 'tests.fixtures' -] +pytest_plugins = ["tests.fixtures"] @pytest.fixture(autouse=True) @@ -10,7 +8,7 @@ def enable_db_access(db): pass -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def django_db_setup(django_db_setup, django_db_blocker): from wagtail.core.models import Page, Site diff --git a/tests/factories/page.py b/tests/factories/page.py index 4731f2e..88127aa 100644 --- a/tests/factories/page.py +++ b/tests/factories/page.py @@ -15,13 +15,14 @@ try: class Meta: model = Locale + except ImportError: pass class ContentPageFactory(PageFactory): parent = None - title = 'Test page' + title = "Test page" slug = factory.LazyAttribute(lambda obj: slugify(obj.title)) class Meta: @@ -29,7 +30,7 @@ class ContentPageFactory(PageFactory): class RegularPageFactory(PageFactory): - title = 'Regular page' + title = "Regular page" slug = factory.LazyAttribute(lambda obj: slugify(obj.title)) class Meta: @@ -37,6 +38,5 @@ class RegularPageFactory(PageFactory): class PersonalisablePageMetadataFactory(factory.DjangoModelFactory): - class Meta: model = PersonalisablePageMetadata diff --git a/tests/factories/rule.py b/tests/factories/rule.py index ef8665c..44ea0b5 100644 --- a/tests/factories/rule.py +++ b/tests/factories/rule.py @@ -8,19 +8,16 @@ from wagtail_personalisation import rules class DayRuleFactory(factory.DjangoModelFactory): - class Meta: model = rules.DayRule class DeviceRuleFactory(factory.DjangoModelFactory): - class Meta: model = rules.DeviceRule class QueryRuleFactory(factory.DjangoModelFactory): - class Meta: model = rules.QueryRule diff --git a/tests/factories/segment.py b/tests/factories/segment.py index f92f579..26ba812 100644 --- a/tests/factories/segment.py +++ b/tests/factories/segment.py @@ -6,7 +6,7 @@ from wagtail_personalisation import models class SegmentFactory(factory.DjangoModelFactory): - name = 'TestSegment' + name = "TestSegment" status = models.Segment.STATUS_ENABLED class Meta: diff --git a/tests/factories/site.py b/tests/factories/site.py index 631963b..7242c59 100644 --- a/tests/factories/site.py +++ b/tests/factories/site.py @@ -5,9 +5,9 @@ from tests.factories.page import ContentPageFactory class SiteFactory(factory.DjangoModelFactory): - hostname = 'localhost' + hostname = "localhost" port = factory.Sequence(lambda n: 81 + n) - site_name = 'Test site' + site_name = "Test site" root_page = factory.SubFactory(ContentPageFactory, parent=None) is_default_site = False diff --git a/tests/fixtures.py b/tests/fixtures.py index 8775f0d..b82403d 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -9,23 +9,23 @@ from tests.factories.segment import SegmentFactory from tests.factories.site import SiteFactory -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def site(): - root_page = ContentPageFactory(parent=None, slug='') + root_page = ContentPageFactory(parent=None, slug="") site = SiteFactory(is_default_site=True, root_page=root_page) - page1 = ContentPageFactory(parent=root_page, slug='page-1') - page2 = ContentPageFactory(parent=root_page, slug='page-2') - ContentPageFactory(parent=page1, slug='page-1-1') - ContentPageFactory(parent=page2, slug='page-2-1') + page1 = ContentPageFactory(parent=root_page, slug="page-1") + page2 = ContentPageFactory(parent=root_page, slug="page-2") + ContentPageFactory(parent=page1, slug="page-1-1") + ContentPageFactory(parent=page2, slug="page-2-1") - RegularPageFactory(parent=root_page, slug='regular') + RegularPageFactory(parent=root_page, slug="regular") return site @pytest.fixture() def segmented_page(site): - page = ContentPageFactory(parent=site.root_page, slug='personalised') + page = ContentPageFactory(parent=site.root_page, slug="personalised") segment = SegmentFactory() return page.personalisation_metadata.copy_for_segment(segment) @@ -37,7 +37,6 @@ def rf(): class RequestFactory(BaseRequestFactory): - def request(self, user=None, **request): request = super(RequestFactory, self).request(**request) request.user = AnonymousUser() @@ -48,4 +47,4 @@ class RequestFactory(BaseRequestFactory): @pytest.fixture() def user(django_user_model): - return django_user_model.objects.create(username='user') + return django_user_model.objects.create(username="user") diff --git a/tests/settings.py b/tests/settings.py index 3f50041..c855ced 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -1,92 +1,86 @@ import os DATABASES = { - 'default': { - 'ENGINE': os.environ.get('DATABASE_ENGINE', 'django.db.backends.sqlite3'), - 'NAME': os.environ.get('DATABASE_NAME', 'db.sqlite3'), + "default": { + "ENGINE": os.environ.get("DATABASE_ENGINE", "django.db.backends.sqlite3"), + "NAME": os.environ.get("DATABASE_NAME", "db.sqlite3"), } } -ALLOWED_HOSTS = ['localhost'] +ALLOWED_HOSTS = ["localhost"] CACHES = { - 'default': { - 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', - 'LOCATION': 'unique-snowflake', + "default": { + "BACKEND": "django.core.cache.backends.locmem.LocMemCache", + "LOCATION": "unique-snowflake", } } -SECRET_KEY = 'not needed' -SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' +SECRET_KEY = "not needed" +SESSION_ENGINE = "django.contrib.sessions.backends.cached_db" -ROOT_URLCONF = 'tests.site.urls' +ROOT_URLCONF = "tests.site.urls" -STATIC_URL = '/static/' +STATIC_URL = "/static/" -STATICFILES_FINDERS = ( - 'django.contrib.staticfiles.finders.AppDirectoriesFinder', -) +STATICFILES_FINDERS = ("django.contrib.staticfiles.finders.AppDirectoriesFinder",) USE_TZ = False TESTS_ROOT = os.path.dirname(os.path.abspath(__file__)) TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [ - os.path.join(TESTS_ROOT, 'site', 'templates'), + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [ + os.path.join(TESTS_ROOT, "site", "templates"), ], - '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', - 'django.template.context_processors.request', + "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", + "django.template.context_processors.request", ], - 'debug': True, + "debug": True, }, }, ] MIDDLEWARE = ( - 'django.middleware.common.CommonMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', + "django.middleware.common.CommonMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", ) INSTALLED_APPS = ( - 'wagtail_personalisation', - - 'wagtail.contrib.modeladmin', - 'wagtail.search', - 'wagtail.sites', - 'wagtail.users', - 'wagtail.images', - 'wagtail.documents', - 'wagtail.admin', - 'wagtail.core', - - 'taggit', - - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - - 'tests.site.pages', + "wagtail_personalisation", + "wagtail.contrib.modeladmin", + "wagtail.search", + "wagtail.sites", + "wagtail.users", + "wagtail.images", + "wagtail.documents", + "wagtail.admin", + "wagtail.core", + "taggit", + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "tests.site.pages", ) PASSWORD_HASHERS = ( - 'django.contrib.auth.hashers.MD5PasswordHasher', # don't use the intentionally slow default password hasher + "django.contrib.auth.hashers.MD5PasswordHasher", # don't use the intentionally slow default password hasher ) -WAGTAIL_SITE_NAME = 'wagtail-personalisation test' +WAGTAIL_SITE_NAME = "wagtail-personalisation test" diff --git a/tests/site/pages/migrations/0001_initial.py b/tests/site/pages/migrations/0001_initial.py index bbd7542..b40b287 100644 --- a/tests/site/pages/migrations/0001_initial.py +++ b/tests/site/pages/migrations/0001_initial.py @@ -14,20 +14,33 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('wagtailcore', '0001_initial'), + ("wagtailcore", "0001_initial"), ] operations = [ migrations.CreateModel( - name='ContentPage', + name="ContentPage", fields=[ - ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Page')), # noqa: E501 - ('subtitle', models.CharField(blank=True, default='', max_length=255)), - ('body', wagtail.core.fields.RichTextField(blank=True, default='')), + ( + "page_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="wagtailcore.Page", + ), + ), # noqa: E501 + ("subtitle", models.CharField(blank=True, default="", max_length=255)), + ("body", wagtail.core.fields.RichTextField(blank=True, default="")), ], options={ - 'abstract': False, + "abstract": False, }, - bases=(wagtail_personalisation.models.PersonalisablePageMixin, 'wagtailcore.page'), + bases=( + wagtail_personalisation.models.PersonalisablePageMixin, + "wagtailcore.page", + ), ), ] diff --git a/tests/site/pages/migrations/0002_regularpage.py b/tests/site/pages/migrations/0002_regularpage.py index df2697b..0bd6b8d 100644 --- a/tests/site/pages/migrations/0002_regularpage.py +++ b/tests/site/pages/migrations/0002_regularpage.py @@ -9,21 +9,31 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('wagtailcore', '0001_initial'), - ('pages', '0001_initial'), + ("wagtailcore", "0001_initial"), + ("pages", "0001_initial"), ] operations = [ migrations.CreateModel( - name='RegularPage', + name="RegularPage", fields=[ - ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Page')), # noqa: E501 - ('subtitle', models.CharField(blank=True, default='', max_length=255)), - ('body', wagtail.core.fields.RichTextField(blank=True, default='')), + ( + "page_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="wagtailcore.Page", + ), + ), # noqa: E501 + ("subtitle", models.CharField(blank=True, default="", max_length=255)), + ("body", wagtail.core.fields.RichTextField(blank=True, default="")), ], options={ - 'abstract': False, + "abstract": False, }, - bases=('wagtailcore.page',), + bases=("wagtailcore.page",), ), ] diff --git a/tests/site/pages/models.py b/tests/site/pages/models.py index 6c40d92..ecfa29d 100644 --- a/tests/site/pages/models.py +++ b/tests/site/pages/models.py @@ -7,20 +7,20 @@ from wagtail_personalisation.models import PersonalisablePageMixin class RegularPage(Page): - subtitle = models.CharField(max_length=255, blank=True, default='') - body = RichTextField(blank=True, default='') + subtitle = models.CharField(max_length=255, blank=True, default="") + body = RichTextField(blank=True, default="") content_panels = Page.content_panels + [ - FieldPanel('subtitle'), - FieldPanel('body'), + FieldPanel("subtitle"), + FieldPanel("body"), ] class ContentPage(PersonalisablePageMixin, Page): - subtitle = models.CharField(max_length=255, blank=True, default='') - body = RichTextField(blank=True, default='') + subtitle = models.CharField(max_length=255, blank=True, default="") + body = RichTextField(blank=True, default="") content_panels = Page.content_panels + [ - FieldPanel('subtitle'), - FieldPanel('body'), + FieldPanel("subtitle"), + FieldPanel("body"), ] diff --git a/tests/site/urls.py b/tests/site/urls.py index b13e9cb..9b49168 100644 --- a/tests/site/urls.py +++ b/tests/site/urls.py @@ -7,10 +7,8 @@ from wagtail.core import urls as wagtail_urls from wagtail.documents import urls as wagtaildocs_urls urlpatterns = [ - url(r'^django-admin/', admin.site.urls), - - url(r'^admin/', include(wagtailadmin_urls)), - url(r'^documents/', include(wagtaildocs_urls)), - - url(r'', include(wagtail_urls)), + url(r"^django-admin/", admin.site.urls), + url(r"^admin/", include(wagtailadmin_urls)), + url(r"^documents/", include(wagtaildocs_urls)), + url(r"", include(wagtail_urls)), ] diff --git a/tests/unit/test_adapter_session.py b/tests/unit/test_adapter_session.py index 191b20a..6bf03ef 100644 --- a/tests/unit/test_adapter_session.py +++ b/tests/unit/test_adapter_session.py @@ -6,15 +6,15 @@ from wagtail_personalisation import adapters @pytest.mark.django_db def test_get_segments(rf): - request = rf.get('/') + request = rf.get("/") adapter = adapters.SessionSegmentsAdapter(request) - segment_1 = SegmentFactory(name='segment-1', persistent=True) - segment_2 = SegmentFactory(name='segment-2', persistent=True) + segment_1 = SegmentFactory(name="segment-1", persistent=True) + segment_2 = SegmentFactory(name="segment-2", persistent=True) adapter.set_segments([segment_1, segment_2]) - assert len(request.session['segments']) == 2 + assert len(request.session["segments"]) == 2 segments = adapter.get_segments() assert segments == [segment_1, segment_2] @@ -22,15 +22,15 @@ def test_get_segments(rf): @pytest.mark.django_db def test_get_segments_session(rf): - request = rf.get('/') + request = rf.get("/") adapter = adapters.SessionSegmentsAdapter(request) - segment_1 = SegmentFactory(name='segment-1', persistent=True) - segment_2 = SegmentFactory(name='segment-2', persistent=True) + segment_1 = SegmentFactory(name="segment-1", persistent=True) + segment_2 = SegmentFactory(name="segment-2", persistent=True) adapter.set_segments([segment_1, segment_2]) - assert len(request.session['segments']) == 2 + assert len(request.session["segments"]) == 2 adapter._segment_cache = None segments = adapter.get_segments() @@ -39,12 +39,12 @@ def test_get_segments_session(rf): @pytest.mark.django_db def test_get_segment_by_id(rf): - request = rf.get('/') + request = rf.get("/") adapter = adapters.SessionSegmentsAdapter(request) - segment_1 = SegmentFactory(name='segment-1', persistent=True) - segment_2 = SegmentFactory(name='segment-2', persistent=True) + segment_1 = SegmentFactory(name="segment-1", persistent=True) + segment_2 = SegmentFactory(name="segment-2", persistent=True) adapter.set_segments([segment_1, segment_2]) @@ -54,12 +54,12 @@ def test_get_segment_by_id(rf): @pytest.mark.django_db def test_refresh_removes_disabled(rf): - request = rf.get('/') + request = rf.get("/") adapter = adapters.SessionSegmentsAdapter(request) - segment_1 = SegmentFactory(name='segment-1', persistent=True) - segment_2 = SegmentFactory(name='segment-2', persistent=True) + segment_1 = SegmentFactory(name="segment-1", persistent=True) + segment_2 = SegmentFactory(name="segment-2", persistent=True) adapter.set_segments([segment_1, segment_2]) @@ -73,27 +73,27 @@ def test_refresh_removes_disabled(rf): @pytest.mark.django_db def test_add_page_visit(rf, site): - request = rf.get('/') + request = rf.get("/") adapter = adapters.SessionSegmentsAdapter(request) adapter.add_page_visit(site.root_page) - assert request.session['visit_count'][0]['count'] == 1 + assert request.session["visit_count"][0]["count"] == 1 adapter.add_page_visit(site.root_page) - assert request.session['visit_count'][0]['count'] == 2 + assert request.session["visit_count"][0]["count"] == 2 assert adapter.get_visit_count() == 2 @pytest.mark.django_db def test_update_visit_count(rf, site): - request = rf.get('/') + request = rf.get("/") adapter = adapters.SessionSegmentsAdapter(request) - segment_1 = SegmentFactory(name='segment-1', persistent=True, visit_count=0) - segment_2 = SegmentFactory(name='segment-2', persistent=True, visit_count=0) + segment_1 = SegmentFactory(name="segment-1", persistent=True, visit_count=0) + segment_2 = SegmentFactory(name="segment-2", persistent=True, visit_count=0) adapter.set_segments([segment_1, segment_2]) adapter.update_visit_count() @@ -107,12 +107,12 @@ def test_update_visit_count(rf, site): @pytest.mark.django_db def test_update_visit_count_deleted_segment(rf, site): - request = rf.get('/') + request = rf.get("/") adapter = adapters.SessionSegmentsAdapter(request) - segment_1 = SegmentFactory(name='segment-1', persistent=True, visit_count=0) - segment_2 = SegmentFactory(name='segment-2', persistent=True, visit_count=0) + segment_1 = SegmentFactory(name="segment-1", persistent=True, visit_count=0) + segment_2 = SegmentFactory(name="segment-2", persistent=True, visit_count=0) adapter.set_segments([segment_1, segment_2]) segment_2.delete() diff --git a/tests/unit/test_factories.py b/tests/unit/test_factories.py index d82efba..31e5a2e 100644 --- a/tests/unit/test_factories.py +++ b/tests/unit/test_factories.py @@ -15,11 +15,12 @@ from wagtail_personalisation.rules import TimeRule @pytest.mark.django_db def test_segment_create(): factoried_segment = SegmentFactory() - segment = Segment(name='TestSegment', status='enabled') + segment = Segment(name="TestSegment", status="enabled") TimeRule( start_time=datetime.time(8, 0, 0), end_time=datetime.time(23, 0, 0), - segment=segment) + segment=segment, + ) assert factoried_segment.name == segment.name assert factoried_segment.status == segment.status @@ -27,21 +28,16 @@ def test_segment_create(): @pytest.mark.django_db def test_referral_rule_create(): - segment = SegmentFactory(name='Referral') - referral_rule = ReferralRuleFactory( - regex_string='test.test', - segment=segment) + segment = SegmentFactory(name="Referral") + referral_rule = ReferralRuleFactory(regex_string="test.test", segment=segment) - assert referral_rule.regex_string == 'test.test' + assert referral_rule.regex_string == "test.test" @pytest.mark.django_db def test_query_rule_create(): - segment = SegmentFactory(name='Query') - query_rule = QueryRuleFactory( - parameter="query", - value="value", - segment=segment) + segment = SegmentFactory(name="Query") + query_rule = QueryRuleFactory(parameter="query", value="value", segment=segment) - assert query_rule.parameter == 'query' - assert query_rule.value == 'value' + assert query_rule.parameter == "query" + assert query_rule.value == "value" diff --git a/tests/unit/test_models.py b/tests/unit/test_models.py index e15571a..9ddded1 100644 --- a/tests/unit/test_models.py +++ b/tests/unit/test_models.py @@ -16,7 +16,8 @@ def test_segment_create(): TimeRule( start_time=datetime.time(8, 0, 0), end_time=datetime.time(23, 0, 0), - segment=segment) + segment=segment, + ) @pytest.mark.django_db diff --git a/tests/unit/test_regressions.py b/tests/unit/test_regressions.py index 8adffa3..f208bbb 100644 --- a/tests/unit/test_regressions.py +++ b/tests/unit/test_regressions.py @@ -3,5 +3,5 @@ import pytest @pytest.mark.django_db def test_request_device_segment_no_match(client, site): - response = client.get('/regular/') + response = client.get("/regular/") assert response.status_code == 200 diff --git a/tests/unit/test_rules_country_origin.py b/tests/unit/test_rules_country_origin.py index 8ee82f2..64b2486 100644 --- a/tests/unit/test_rules_country_origin.py +++ b/tests/unit/test_rules_country_origin.py @@ -8,71 +8,72 @@ 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' + find_spec("geoip2"), reason="requires GeoIP2 to be not installed" ) skip_if_geoip2_not_installed = pytest.mark.skipif( - not find_spec('geoip2'), reason='requires GeoIP2 to be installed.' + not find_spec("geoip2"), reason="requires GeoIP2 to be installed." ) @pytest.mark.django_db def test_get_cloudflare_country_with_header(rf): - segment = SegmentFactory(name='Test segment') - rule = OriginCountryRuleFactory(segment=segment, country='GB') - request = rf.get('/', HTTP_CF_IPCOUNTRY='PL') - assert rule.get_cloudflare_country(request) == 'pl' + segment = SegmentFactory(name="Test segment") + rule = OriginCountryRuleFactory(segment=segment, country="GB") + request = rf.get("/", HTTP_CF_IPCOUNTRY="PL") + assert rule.get_cloudflare_country(request) == "pl" @pytest.mark.django_db def test_get_cloudflare_country_with_no_header(rf): - segment = SegmentFactory(name='Test segment') - rule = OriginCountryRuleFactory(segment=segment, country='GB') - request = rf.get('/') - assert 'HTTP_CF_IPCOUNTRY' not in request.META + segment = SegmentFactory(name="Test segment") + rule = OriginCountryRuleFactory(segment=segment, country="GB") + request = rf.get("/") + assert "HTTP_CF_IPCOUNTRY" not in request.META assert rule.get_cloudflare_country(request) is None @pytest.mark.django_db def test_get_cloudfront_country_with_header(rf): - segment = SegmentFactory(name='Test segment') - rule = OriginCountryRuleFactory(segment=segment, country='GB') - request = rf.get('/', HTTP_CLOUDFRONT_VIEWER_COUNTRY='BY') - assert rule.get_cloudfront_country(request) == 'by' + segment = SegmentFactory(name="Test segment") + rule = OriginCountryRuleFactory(segment=segment, country="GB") + request = rf.get("/", HTTP_CLOUDFRONT_VIEWER_COUNTRY="BY") + assert rule.get_cloudfront_country(request) == "by" @pytest.mark.django_db def test_get_cloudfront_country_with_no_header(rf): - segment = SegmentFactory(name='Test segment') - rule = OriginCountryRuleFactory(segment=segment, country='GB') - request = rf.get('/') - assert 'HTTP_CLOUDFRONT_VIEWER_COUNTRY' not in request.META + segment = SegmentFactory(name="Test segment") + rule = OriginCountryRuleFactory(segment=segment, country="GB") + request = rf.get("/") + assert "HTTP_CLOUDFRONT_VIEWER_COUNTRY" not in request.META assert rule.get_cloudfront_country(request) is None @pytest.mark.django_db def test_get_geoip_country_with_remote_addr(rf): - segment = SegmentFactory(name='Test segment') - rule = OriginCountryRuleFactory(segment=segment, country='GB') - request = rf.get('/', REMOTE_ADDR='173.254.89.34') + segment = SegmentFactory(name="Test segment") + rule = OriginCountryRuleFactory(segment=segment, country="GB") + request = rf.get("/", REMOTE_ADDR="173.254.89.34") geoip_mock = MagicMock() - with patch('wagtail_personalisation.rules.get_geoip_module', - return_value=geoip_mock) as geoip_import_mock: + with patch( + "wagtail_personalisation.rules.get_geoip_module", return_value=geoip_mock + ) as geoip_import_mock: rule.get_geoip_country(request) geoip_import_mock.assert_called_once() geoip_mock.assert_called_once() - assert geoip_mock.mock_calls[1] == call().country_code('173.254.89.34') + assert geoip_mock.mock_calls[1] == call().country_code("173.254.89.34") @pytest.mark.django_db def test_get_country_calls_all_methods(rf): - segment = SegmentFactory(name='Test segment') - rule = OriginCountryRuleFactory(segment=segment, country='GB') - request = rf.get('/') + segment = SegmentFactory(name="Test segment") + rule = OriginCountryRuleFactory(segment=segment, country="GB") + request = rf.get("/") - @patch.object(rule, 'get_geoip_country', return_value='') - @patch.object(rule, 'get_cloudflare_country', return_value='') - @patch.object(rule, 'get_cloudfront_country', return_value='') + @patch.object(rule, "get_geoip_country", return_value="") + @patch.object(rule, "get_cloudflare_country", return_value="") + @patch.object(rule, "get_cloudfront_country", return_value="") def test_mock(cloudfront_mock, cloudflare_mock, geoip_mock): country = rule.get_country(request) cloudflare_mock.assert_called_once_with(request) @@ -85,107 +86,106 @@ def test_get_country_calls_all_methods(rf): @pytest.mark.django_db def test_get_country_does_not_use_all_detection_methods_unnecessarily(rf): - segment = SegmentFactory(name='Test segment') - rule = OriginCountryRuleFactory(segment=segment, country='GB') - request = rf.get('/') + segment = SegmentFactory(name="Test segment") + rule = OriginCountryRuleFactory(segment=segment, country="GB") + request = rf.get("/") - @patch.object(rule, 'get_geoip_country', return_value='') - @patch.object(rule, 'get_cloudflare_country', return_value='t1') - @patch.object(rule, 'get_cloudfront_country', return_value='') + @patch.object(rule, "get_geoip_country", return_value="") + @patch.object(rule, "get_cloudflare_country", return_value="t1") + @patch.object(rule, "get_cloudfront_country", return_value="") def test_mock(cloudfront_mock, cloudflare_mock, geoip_mock): country = rule.get_country(request) cloudflare_mock.assert_called_once_with(request) cloudfront_mock.assert_not_called() geoip_mock.assert_not_called() - assert country == 't1' + assert country == "t1" test_mock() @pytest.mark.django_db def test_test_user_calls_get_country(rf): - segment = SegmentFactory(name='Test segment') - rule = OriginCountryRuleFactory(segment=segment, country='GB') - request = rf.get('/') - with patch.object(rule, 'get_country') as get_country_mock: + segment = SegmentFactory(name="Test segment") + rule = OriginCountryRuleFactory(segment=segment, country="GB") + request = rf.get("/") + with patch.object(rule, "get_country") as get_country_mock: rule.test_user(request) get_country_mock.assert_called_once_with(request) @pytest.mark.django_db def test_test_user_returns_true_if_cloudflare_country_match(rf): - segment = SegmentFactory(name='Test segment') - rule = OriginCountryRuleFactory(segment=segment, country='GB') - request = rf.get('/', HTTP_CF_IPCOUNTRY='GB') + segment = SegmentFactory(name="Test segment") + rule = OriginCountryRuleFactory(segment=segment, country="GB") + request = rf.get("/", HTTP_CF_IPCOUNTRY="GB") assert rule.test_user(request) is True @pytest.mark.django_db def test_test_user_returns_false_if_cloudflare_country_doesnt_match(rf): - segment = SegmentFactory(name='Test segment') - rule = OriginCountryRuleFactory(segment=segment, country='GB') - request = rf.get('/', HTTP_CF_IPCOUNTRY='NL') + segment = SegmentFactory(name="Test segment") + rule = OriginCountryRuleFactory(segment=segment, country="GB") + request = rf.get("/", HTTP_CF_IPCOUNTRY="NL") assert not rule.test_user(request) @pytest.mark.django_db def test_test_user_returns_false_if_cloudfront_country_doesnt_match(rf): - segment = SegmentFactory(name='Test segment') - rule = OriginCountryRuleFactory(segment=segment, country='GB') - request = rf.get('/', HTTP_CLOUDFRONT_VIEWER_COUNTRY='NL') + segment = SegmentFactory(name="Test segment") + rule = OriginCountryRuleFactory(segment=segment, country="GB") + request = rf.get("/", HTTP_CLOUDFRONT_VIEWER_COUNTRY="NL") assert rule.test_user(request) is False @pytest.mark.django_db def test_test_user_returns_true_if_cloudfront_country_matches(rf): - segment = SegmentFactory(name='Test segment') - rule = OriginCountryRuleFactory(segment=segment, country='se') - request = rf.get('/', HTTP_CLOUDFRONT_VIEWER_COUNTRY='SE') + segment = SegmentFactory(name="Test segment") + rule = OriginCountryRuleFactory(segment=segment, country="se") + request = rf.get("/", HTTP_CLOUDFRONT_VIEWER_COUNTRY="SE") assert rule.test_user(request) is True @skip_if_geoip2_not_installed @pytest.mark.django_db def test_test_user_geoip_module_matches(rf): - segment = SegmentFactory(name='Test segment') - rule = OriginCountryRuleFactory(segment=segment, country='se') - request = rf.get('/', REMOTE_ADDR='123.120.0.2') + segment = SegmentFactory(name="Test segment") + rule = OriginCountryRuleFactory(segment=segment, country="se") + request = rf.get("/", REMOTE_ADDR="123.120.0.2") GeoIP2Mock = MagicMock() - GeoIP2Mock().configure_mock(**{'country_code.return_value': 'SE'}) + GeoIP2Mock().configure_mock(**{"country_code.return_value": "SE"}) GeoIP2Mock.reset_mock() - with patch('wagtail_personalisation.rules.get_geoip_module', - return_value=GeoIP2Mock): + with patch( + "wagtail_personalisation.rules.get_geoip_module", return_value=GeoIP2Mock + ): assert rule.test_user(request) is True assert GeoIP2Mock.mock_calls == [ call(), - call().country_code('123.120.0.2'), + call().country_code("123.120.0.2"), ] @skip_if_geoip2_not_installed @pytest.mark.django_db def test_test_user_geoip_module_does_not_match(rf): - segment = SegmentFactory(name='Test segment') - rule = OriginCountryRuleFactory(segment=segment, country='nl') - request = rf.get('/', REMOTE_ADDR='123.120.0.2') + segment = SegmentFactory(name="Test segment") + rule = OriginCountryRuleFactory(segment=segment, country="nl") + request = rf.get("/", REMOTE_ADDR="123.120.0.2") GeoIP2Mock = MagicMock() - GeoIP2Mock().configure_mock(**{'country_code.return_value': 'SE'}) + GeoIP2Mock().configure_mock(**{"country_code.return_value": "SE"}) GeoIP2Mock.reset_mock() - with patch('wagtail_personalisation.rules.get_geoip_module', - return_value=GeoIP2Mock): + with patch( + "wagtail_personalisation.rules.get_geoip_module", return_value=GeoIP2Mock + ): assert rule.test_user(request) is False - assert GeoIP2Mock.mock_calls == [ - call(), - call().country_code('123.120.0.2') - ] + assert GeoIP2Mock.mock_calls == [call(), call().country_code("123.120.0.2")] @skip_if_geoip2_installed @pytest.mark.django_db def test_test_user_does_not_use_geoip_module_if_disabled(rf): - segment = SegmentFactory(name='Test segment') - rule = OriginCountryRuleFactory(segment=segment, country='se') - request = rf.get('/', REMOTE_ADDR='123.120.0.2') + segment = SegmentFactory(name="Test segment") + rule = OriginCountryRuleFactory(segment=segment, country="se") + request = rf.get("/", REMOTE_ADDR="123.120.0.2") assert rule.test_user(request) is False @@ -199,4 +199,5 @@ def test_get_geoip_module_disabled(): @skip_if_geoip2_not_installed def test_get_geoip_module_enabled(): from django.contrib.gis.geoip2 import GeoIP2 + assert get_geoip_module() is GeoIP2 diff --git a/tests/unit/test_rules_day.py b/tests/unit/test_rules_day.py index f42c715..10090f6 100644 --- a/tests/unit/test_rules_day.py +++ b/tests/unit/test_rules_day.py @@ -9,7 +9,7 @@ from tests.factories.segment import SegmentFactory @pytest.mark.django_db def test_day_rule_create(): - segment = SegmentFactory(name='DaySegment') + segment = SegmentFactory(name="DaySegment") day_rule = DayRuleFactory(mon=True, thu=True, segment=segment) assert day_rule.mon is True @@ -20,11 +20,9 @@ def test_day_rule_create(): @pytest.mark.django_db @freeze_time("2017-01-01") def test_request_day_segment(client, site): - day_only_segment = SegmentFactory(name='Day only') - DayRuleFactory( - sun=True, - segment=day_only_segment) + day_only_segment = SegmentFactory(name="Day only") + DayRuleFactory(sun=True, segment=day_only_segment) - client.get('/') + client.get("/") - assert client.session['segments'][0]['encoded_name'] == 'day-only' + assert client.session["segments"][0]["encoded_name"] == "day-only" diff --git a/tests/unit/test_rules_device.py b/tests/unit/test_rules_device.py index f00524d..cb9c1c7 100644 --- a/tests/unit/test_rules_device.py +++ b/tests/unit/test_rules_device.py @@ -6,7 +6,7 @@ from tests.factories.segment import SegmentFactory @pytest.mark.django_db def test_device_rule_create(): - segment = SegmentFactory(name='DeviceSegment') + segment = SegmentFactory(name="DeviceSegment") device_rule = DeviceRuleFactory(mobile=True, segment=segment) assert device_rule.mobile is True @@ -16,23 +16,25 @@ def test_device_rule_create(): @pytest.mark.django_db def test_request_device_segment(client, site): - device_only_segment = SegmentFactory(name='Device only') - DeviceRuleFactory( - tablet=True, - segment=device_only_segment) + device_only_segment = SegmentFactory(name="Device only") + DeviceRuleFactory(tablet=True, segment=device_only_segment) - client.get('/', **{'HTTP_USER_AGENT': 'Mozilla/5.0(iPad; U; CPU iPhone OS 3_2 like Mac OS X)'}) + client.get( + "/", + **{"HTTP_USER_AGENT": "Mozilla/5.0(iPad; U; CPU iPhone OS 3_2 like Mac OS X)"} + ) - assert client.session['segments'][0]['encoded_name'] == 'device-only' + assert client.session["segments"][0]["encoded_name"] == "device-only" @pytest.mark.django_db def test_request_device_segment_no_match(client, site): - no_device_segment = SegmentFactory(name='No device') - DeviceRuleFactory( - mobile=True, - segment=no_device_segment) + no_device_segment = SegmentFactory(name="No device") + DeviceRuleFactory(mobile=True, segment=no_device_segment) - client.get('/', **{'HTTP_USER_AGENT': 'Mozilla/5.0(iPad; U; CPU iPhone OS 3_2 like Mac OS X)'}) + client.get( + "/", + **{"HTTP_USER_AGENT": "Mozilla/5.0(iPad; U; CPU iPhone OS 3_2 like Mac OS X)"} + ) - assert not client.session['segments'] + assert not client.session["segments"] diff --git a/tests/unit/test_rules_mixed.py b/tests/unit/test_rules_mixed.py index 1a38b49..f538e34 100644 --- a/tests/unit/test_rules_mixed.py +++ b/tests/unit/test_rules_mixed.py @@ -11,80 +11,68 @@ from tests.factories.segment import SegmentFactory @pytest.mark.django_db def test_no_segments(client, site): - response = client.get('/') + response = client.get("/") assert response.status_code == 200 - assert client.session['segments'] == [] + assert client.session["segments"] == [] @pytest.mark.django_db def test_referral_segment(client, site): - referral_segment = SegmentFactory(name='Referral') - ReferralRuleFactory( - regex_string="test.test", - segment=referral_segment - ) + referral_segment = SegmentFactory(name="Referral") + ReferralRuleFactory(regex_string="test.test", segment=referral_segment) - response = client.get('/', **{'HTTP_REFERER': 'test.test'}) + response = client.get("/", **{"HTTP_REFERER": "test.test"}) assert response.status_code == 200 - assert client.session['segments'][0]['encoded_name'] == 'referral' + assert client.session["segments"][0]["encoded_name"] == "referral" @pytest.mark.django_db @freeze_time("10:00:00") def test_time_and_referral_segment(client, site): - segment = SegmentFactory(name='Both') + segment = SegmentFactory(name="Both") TimeRuleFactory( start_time=datetime.time(8, 0, 0), end_time=datetime.time(23, 0, 0), - segment=segment - ) - ReferralRuleFactory( - regex_string="test.test", - segment=segment + segment=segment, ) + ReferralRuleFactory(regex_string="test.test", segment=segment) - response = client.get('/', **{'HTTP_REFERER': 'test.test'}) + response = client.get("/", **{"HTTP_REFERER": "test.test"}) assert response.status_code == 200 - assert client.session['segments'][0]['encoded_name'] == 'both' + assert client.session["segments"][0]["encoded_name"] == "both" @pytest.mark.django_db @freeze_time("7:00:00") def test_no_time_but_referral_segment(client, site): - segment = SegmentFactory(name='Not both') + segment = SegmentFactory(name="Not both") TimeRuleFactory( start_time=datetime.time(8, 0, 0), end_time=datetime.time(23, 0, 0), - segment=segment - ) - ReferralRuleFactory( - regex_string="test.test", - segment=segment + segment=segment, ) + ReferralRuleFactory(regex_string="test.test", segment=segment) - response = client.get('/', **{'HTTP_REFERER': 'test.test'}) + response = client.get("/", **{"HTTP_REFERER": "test.test"}) assert response.status_code == 200 - assert len(client.session['segments']) == 0 + assert len(client.session["segments"]) == 0 @pytest.mark.django_db @freeze_time("9:00:00") def test_time_but_no_referral_segment(client, site): - segment = SegmentFactory(name='Not both') + segment = SegmentFactory(name="Not both") TimeRuleFactory( start_time=datetime.time(8, 0, 0), end_time=datetime.time(23, 0, 0), - segment=segment - ) - ReferralRuleFactory( - regex_string="test.test", - segment=segment + segment=segment, ) + ReferralRuleFactory(regex_string="test.test", segment=segment) - response = client.get('/') + response = client.get("/") assert response.status_code == 200 - assert len(client.session['segments']) == 0 + assert len(client.session["segments"]) == 0 diff --git a/tests/unit/test_rules_query.py b/tests/unit/test_rules_query.py index 22a9be5..99ede52 100644 --- a/tests/unit/test_rules_query.py +++ b/tests/unit/test_rules_query.py @@ -6,43 +6,33 @@ from tests.factories.segment import SegmentFactory @pytest.mark.django_db def test_request_query_rule(client, site): - segment = SegmentFactory(name='Query') + segment = SegmentFactory(name="Query") QueryRuleFactory( parameter="query", value="value", segment=segment, ) - response = client.get('/?query=value') + response = client.get("/?query=value") assert response.status_code == 200 - assert any( - item['encoded_name'] == 'query' for item in client.session['segments']) + assert any(item["encoded_name"] == "query" for item in client.session["segments"]) @pytest.mark.django_db def test_request_only_one_query_rule(client, site): - segment = SegmentFactory(name='Query') - QueryRuleFactory( - parameter="query", - value="value", - segment=segment - ) + segment = SegmentFactory(name="Query") + QueryRuleFactory(parameter="query", value="value", segment=segment) - response = client.get('/?test=test&query=value') + response = client.get("/?test=test&query=value") assert response.status_code == 200 - assert any( - item['encoded_name'] == 'query' for item in client.session['segments']) + assert any(item["encoded_name"] == "query" for item in client.session["segments"]) @pytest.mark.django_db def test_request_multiple_queries(client, site): - segment = SegmentFactory(name='Multiple queries') - QueryRuleFactory( - parameter="test", - value="test", - segment=segment - ) + segment = SegmentFactory(name="Multiple queries") + QueryRuleFactory(parameter="test", value="test", segment=segment) QueryRuleFactory( parameter="query", @@ -50,77 +40,65 @@ def test_request_multiple_queries(client, site): segment=segment, ) - response = client.get('/?test=test&query=value') + response = client.get("/?test=test&query=value") assert response.status_code == 200 assert any( - item['encoded_name'] == 'multiple-queries' - for item in client.session['segments'] + item["encoded_name"] == "multiple-queries" + for item in client.session["segments"] ) @pytest.mark.django_db def test_request_persistent_segmenting(client, site): - segment = SegmentFactory(name='Persistent', persistent=True) - QueryRuleFactory( - parameter="test", - value="test", - segment=segment + segment = SegmentFactory(name="Persistent", persistent=True) + QueryRuleFactory(parameter="test", value="test", segment=segment) + + response = client.get("/?test=test") + assert response.status_code == 200 + + assert any( + item["encoded_name"] == "persistent" for item in client.session["segments"] ) - response = client.get('/?test=test') - assert response.status_code == 200 - - assert any( - item['encoded_name'] == 'persistent' - for item in client.session['segments']) - - response = client.get('/') + response = client.get("/") assert response.status_code == 200 assert any( - item['encoded_name'] == 'persistent' - for item in client.session['segments']) + item["encoded_name"] == "persistent" for item in client.session["segments"] + ) @pytest.mark.django_db def test_request_non_persistent_segmenting(client, site): - segment = SegmentFactory(name='Non Persistent') - QueryRuleFactory( - parameter="test", - value="test", - segment=segment - ) + segment = SegmentFactory(name="Non Persistent") + QueryRuleFactory(parameter="test", value="test", segment=segment) - response = client.get('/?test=test') + response = client.get("/?test=test") assert response.status_code == 200 assert any( - item['encoded_name'] == 'non-persistent' - for item in client.session['segments']) + item["encoded_name"] == "non-persistent" for item in client.session["segments"] + ) - response = client.get('/') + response = client.get("/") assert response.status_code == 200 assert not any( - item['encoded_name'] == 'non-persistent' - for item in client.session['segments']) + item["encoded_name"] == "non-persistent" for item in client.session["segments"] + ) @pytest.mark.django_db def test_request_match_any_segmenting(client, site): - segment = SegmentFactory(name='Match any', match_any=True) + segment = SegmentFactory(name="Match any", match_any=True) QueryRuleFactory( - parameter='test', - value='test', + parameter="test", + value="test", segment=segment, ) - QueryRuleFactory( - parameter='test2', - value='test2', - segment=segment - ) + QueryRuleFactory(parameter="test2", value="test2", segment=segment) - response = client.get('/?test=test') + response = client.get("/?test=test") assert response.status_code == 200 assert any( - item['encoded_name'] == 'match-any' - for item in client.session['segments']) + item["encoded_name"] == "match-any" for item in client.session["segments"] + ) diff --git a/tests/unit/test_rules_referral.py b/tests/unit/test_rules_referral.py index b1378e2..fb6faf4 100644 --- a/tests/unit/test_rules_referral.py +++ b/tests/unit/test_rules_referral.py @@ -1,4 +1,3 @@ - import pytest from tests.factories.rule import ReferralRuleFactory @@ -7,9 +6,7 @@ from tests.factories.segment import SegmentFactory @pytest.mark.django_db def test_referral_rule_create(): - segment = SegmentFactory(name='Referral') - referral_rule = ReferralRuleFactory( - regex_string='test.test', - segment=segment) + segment = SegmentFactory(name="Referral") + referral_rule = ReferralRuleFactory(regex_string="test.test", segment=segment) - assert referral_rule.regex_string == 'test.test' + assert referral_rule.regex_string == "test.test" diff --git a/tests/unit/test_rules_time.py b/tests/unit/test_rules_time.py index f1fa005..09ac35f 100644 --- a/tests/unit/test_rules_time.py +++ b/tests/unit/test_rules_time.py @@ -9,11 +9,12 @@ from tests.factories.segment import SegmentFactory @pytest.mark.django_db def test_time_rule_create(): - segment = SegmentFactory(name='TimeSegment') + segment = SegmentFactory(name="TimeSegment") time_rule = TimeRuleFactory( start_time=datetime.time(8, 0, 0), end_time=datetime.time(23, 0, 0), - segment=segment) + segment=segment, + ) assert time_rule.start_time == datetime.time(8, 0, 0) @@ -21,13 +22,14 @@ def test_time_rule_create(): @pytest.mark.django_db @freeze_time("10:00:00") def test_requesttime_segment(client, site): - time_only_segment = SegmentFactory(name='Time only') + time_only_segment = SegmentFactory(name="Time only") TimeRuleFactory( start_time=datetime.time(8, 0, 0), end_time=datetime.time(23, 0, 0), - segment=time_only_segment) + segment=time_only_segment, + ) - response = client.get('/') + response = client.get("/") assert response.status_code == 200 - assert client.session['segments'][0]['encoded_name'] == 'time-only' + assert client.session["segments"][0]["encoded_name"] == "time-only" diff --git a/tests/unit/test_rules_visitcount.py b/tests/unit/test_rules_visitcount.py index 321f043..ece9709 100644 --- a/tests/unit/test_rules_visitcount.py +++ b/tests/unit/test_rules_visitcount.py @@ -7,23 +7,23 @@ from wagtail_personalisation.rules import VisitCountRule @pytest.mark.django_db def test_visit_count(site, client): - response = client.get('/') + response = client.get("/") assert response.status_code == 200 - visit_count = client.session['visit_count'] - assert visit_count[0]['path'] == '/' - assert visit_count[0]['count'] == 1 + visit_count = client.session["visit_count"] + assert visit_count[0]["path"] == "/" + assert visit_count[0]["count"] == 1 - response = client.get('/') + response = client.get("/") assert response.status_code == 200 - visit_count = client.session['visit_count'] - assert visit_count[0]['path'] == '/' - assert visit_count[0]['count'] == 2 + visit_count = client.session["visit_count"] + assert visit_count[0]["path"] == "/" + assert visit_count[0]["count"] == 2 - response = client.get('/page-1/') + response = client.get("/page-1/") assert response.status_code == 200 - visit_count = client.session['visit_count'] - assert visit_count[0]['count'] == 2 - assert visit_count[1]['count'] == 1 + visit_count = client.session["visit_count"] + assert visit_count[0]["count"] == 2 + assert visit_count[1]["count"] == 1 @pytest.mark.django_db @@ -34,11 +34,11 @@ def test_call_test_user_on_invalid_rule_fails(site, user, mocker): @pytest.mark.django_db def test_visit_count_call_test_user_with_user(site, client, user): - segment = SegmentFactory(name='VisitCount') + segment = SegmentFactory(name="VisitCount") rule = VisitCountRuleFactory(counted_page=site.root_page, segment=segment) session = client.session - session['visit_count'] = [{'path': '/', 'count': 2}] + session["visit_count"] = [{"path": "/", "count": 2}] session.save() client.force_login(user) @@ -47,11 +47,11 @@ def test_visit_count_call_test_user_with_user(site, client, user): @pytest.mark.django_db def test_visit_count_call_test_user_with_user_or_request_fails(site, client, user): - segment = SegmentFactory(name='VisitCount') + segment = SegmentFactory(name="VisitCount") rule = VisitCountRuleFactory(counted_page=site.root_page, segment=segment) session = client.session - session['visit_count'] = [{'path': '/', 'count': 2}] + session["visit_count"] = [{"path": "/", "count": 2}] session.save() client.force_login(user) @@ -60,20 +60,20 @@ def test_visit_count_call_test_user_with_user_or_request_fails(site, client, use @pytest.mark.django_db def test_get_column_header(site): - segment = SegmentFactory(name='VisitCount') + segment = SegmentFactory(name="VisitCount") rule = VisitCountRuleFactory(counted_page=site.root_page, segment=segment) - assert rule.get_column_header() == 'Visit count - Test page' + assert rule.get_column_header() == "Visit count - Test page" @pytest.mark.django_db def test_get_user_info_string_returns_count(site, client, user): - segment = SegmentFactory(name='VisitCount') + segment = SegmentFactory(name="VisitCount") rule = VisitCountRuleFactory(counted_page=site.root_page, segment=segment) session = client.session - session['visit_count'] = [{'path': '/', 'count': 2}] + session["visit_count"] = [{"path": "/", "count": 2}] session.save() client.force_login(user) - assert rule.get_user_info_string(user) == '2' + assert rule.get_user_info_string(user) == "2" diff --git a/tests/unit/test_static_dynamic_segments.py b/tests/unit/test_static_dynamic_segments.py index b69cd4a..c4af4ad 100644 --- a/tests/unit/test_static_dynamic_segments.py +++ b/tests/unit/test_static_dynamic_segments.py @@ -12,7 +12,14 @@ from wagtail_personalisation.rules import TimeRule, VisitCountRule def form_with_data(segment, *rules): - model_fields = ['type', 'status', 'count', 'name', 'match_any', 'randomisation_percent'] + model_fields = [ + "type", + "status", + "count", + "name", + "match_any", + "randomisation_percent", + ] class TestSegmentAdminForm(SegmentAdminForm): class Meta: @@ -27,10 +34,10 @@ def form_with_data(segment, *rules): if isinstance(rule, formset.model): rule_data = model_to_dict(rule) for key, value in rule_data.items(): - data['{}-{}-{}'.format(formset.prefix, count, key)] = value + data["{}-{}-{}".format(formset.prefix, count, key)] = value count += 1 - data['{}-INITIAL_FORMS'.format(formset.prefix)] = 0 - data['{}-TOTAL_FORMS'.format(formset.prefix)] = count + data["{}-INITIAL_FORMS".format(formset.prefix)] = 0 + data["{}-TOTAL_FORMS".format(formset.prefix)] = count return TestSegmentAdminForm(data) @@ -39,21 +46,27 @@ def test_user_added_to_static_segment_at_creation(site, user, mocker): segment = SegmentFactory.build(type=Segment.TYPE_STATIC) rule = VisitCountRule(counted_page=site.root_page) form = form_with_data(segment, rule) - mocker.patch('wagtail_personalisation.rules.VisitCountRule.test_user', return_value=True) + mocker.patch( + "wagtail_personalisation.rules.VisitCountRule.test_user", return_value=True + ) instance = form.save() assert user in instance.static_users.all() @pytest.mark.django_db -def test_user_not_added_to_full_static_segment_at_creation(site, django_user_model, mocker): - django_user_model.objects.create(username='first') - django_user_model.objects.create(username='second') +def test_user_not_added_to_full_static_segment_at_creation( + site, django_user_model, mocker +): + django_user_model.objects.create(username="first") + django_user_model.objects.create(username="second") segment = SegmentFactory.build(type=Segment.TYPE_STATIC, count=1) rule = VisitCountRule(counted_page=site.root_page) form = form_with_data(segment, rule) - mocker.patch('wagtail_personalisation.rules.VisitCountRule.test_user', - side_effect=[True, True]) + mocker.patch( + "wagtail_personalisation.rules.VisitCountRule.test_user", + side_effect=[True, True], + ) instance = form.save() assert len(instance.static_users.all()) == 1 @@ -68,7 +81,9 @@ def test_anonymous_user_not_added_to_static_segment_at_creation(site, client, mo segment = SegmentFactory.build(type=Segment.TYPE_STATIC) rule = VisitCountRule(counted_page=site.root_page) form = form_with_data(segment, rule) - mock_test_rule = mocker.patch('wagtail_personalisation.adapters.SessionSegmentsAdapter._test_rules') + mock_test_rule = mocker.patch( + "wagtail_personalisation.adapters.SessionSegmentsAdapter._test_rules" + ) instance = form.save() assert not instance.static_users.all() @@ -77,15 +92,18 @@ def test_anonymous_user_not_added_to_static_segment_at_creation(site, client, mo @pytest.mark.django_db def test_match_any_correct_populates(site, django_user_model, mocker): - user = django_user_model.objects.create(username='first') - other_user = django_user_model.objects.create(username='second') + user = django_user_model.objects.create(username="first") + other_user = django_user_model.objects.create(username="second") other_page = site.root_page.get_last_child() segment = SegmentFactory.build(type=Segment.TYPE_STATIC, match_any=True) rule_1 = VisitCountRule(counted_page=site.root_page) rule_2 = VisitCountRule(counted_page=other_page) form = form_with_data(segment, rule_1, rule_2) - mocker.patch('wagtail_personalisation.rules.VisitCountRule.test_user', side_effect=[True, False, True, False]) + mocker.patch( + "wagtail_personalisation.rules.VisitCountRule.test_user", + side_effect=[True, False, True, False], + ) instance = form.save() assert user in instance.static_users.all() @@ -102,7 +120,9 @@ def test_mixed_static_dynamic_session_doesnt_generate_at_creation(site, mocker): ) form = form_with_data(segment, static_rule, non_static_rule) - mock_test_rule = mocker.patch('wagtail_personalisation.adapters.SessionSegmentsAdapter._test_rules') + mock_test_rule = mocker.patch( + "wagtail_personalisation.adapters.SessionSegmentsAdapter._test_rules" + ) instance = form.save() assert not instance.static_users.all() @@ -154,9 +174,11 @@ def test_anonymou_user_not_added_to_static_segment_after_creation(site, client): @pytest.mark.django_db -def test_session_not_added_to_static_segment_after_full(site, client, django_user_model): - user = django_user_model.objects.create(username='first') - other_user = django_user_model.objects.create(username='second') +def test_session_not_added_to_static_segment_after_full( + site, client, django_user_model +): + user = django_user_model.objects.create(username="first") + other_user = django_user_model.objects.create(username="second") segment = SegmentFactory.build(type=Segment.TYPE_STATIC, count=1) rule = VisitCountRule(counted_page=site.root_page) form = form_with_data(segment, rule) @@ -190,7 +212,9 @@ def test_sessions_not_added_to_static_segment_if_rule_not_static(mocker): segment=segment, ) form = form_with_data(segment, rule) - mock_test_rule = mocker.patch('wagtail_personalisation.adapters.SessionSegmentsAdapter._test_rules') + mock_test_rule = mocker.patch( + "wagtail_personalisation.adapters.SessionSegmentsAdapter._test_rules" + ) instance = form.save() assert not instance.static_users.all() @@ -202,12 +226,16 @@ def test_does_not_calculate_the_segment_again(site, client, mocker, user): segment = SegmentFactory.build(type=Segment.TYPE_STATIC, count=2) rule = VisitCountRule(counted_page=site.root_page, segment=segment) form = form_with_data(segment, rule) - mocker.patch('wagtail_personalisation.rules.VisitCountRule.test_user', return_value=True) + mocker.patch( + "wagtail_personalisation.rules.VisitCountRule.test_user", return_value=True + ) instance = form.save() assert user in instance.static_users.all() - mock_test_rule = mocker.patch('wagtail_personalisation.adapters.SessionSegmentsAdapter._test_rules') + mock_test_rule = mocker.patch( + "wagtail_personalisation.adapters.SessionSegmentsAdapter._test_rules" + ) session = client.session session.save() client.force_login(user) @@ -247,7 +275,9 @@ def test_dynamic_segment_with_non_static_rules_have_a_count(): @pytest.mark.django_db -def test_randomisation_percentage_added_to_segment_at_creation(site, client, mocker, django_user_model): +def test_randomisation_percentage_added_to_segment_at_creation( + site, client, mocker, django_user_model +): segment = SegmentFactory.build(type=Segment.TYPE_STATIC) segment.randomisation_percent = 80 rule = VisitCountRule() @@ -280,80 +310,85 @@ def test_randomisation_percentage_max_100(site, client, mocker, django_user_mode @pytest.mark.django_db def test_in_static_segment_if_random_is_below_percentage(site, client, mocker, user): - segment = SegmentFactory.build(type=Segment.TYPE_STATIC, count=1, - randomisation_percent=40) + segment = SegmentFactory.build( + type=Segment.TYPE_STATIC, count=1, randomisation_percent=40 + ) rule = VisitCountRule(counted_page=site.root_page) form = form_with_data(segment, rule) instance = form.save() - mocker.patch('random.randint', return_value=39) + mocker.patch("random.randint", return_value=39) session = client.session session.save() client.force_login(user) client.get(site.root_page.url) - assert instance.id == client.session['segments'][0]['id'] + assert instance.id == client.session["segments"][0]["id"] assert user in instance.static_users.all() assert user not in instance.excluded_users.all() @pytest.mark.django_db -def test_not_in_static_segment_if_random_is_above_percentage(site, client, mocker, user): - segment = SegmentFactory.build(type=Segment.TYPE_STATIC, count=1, - randomisation_percent=40) +def test_not_in_static_segment_if_random_is_above_percentage( + site, client, mocker, user +): + segment = SegmentFactory.build( + type=Segment.TYPE_STATIC, count=1, randomisation_percent=40 + ) rule = VisitCountRule(counted_page=site.root_page) form = form_with_data(segment, rule) instance = form.save() - mocker.patch('random.randint', return_value=41) + mocker.patch("random.randint", return_value=41) session = client.session session.save() client.force_login(user) client.get(site.root_page.url) - assert len(client.session['segments']) == 0 + assert len(client.session["segments"]) == 0 assert user not in instance.static_users.all() assert user in instance.excluded_users.all() @pytest.mark.django_db def test_offered_dynamic_segment_if_random_is_below_percentage(site, client, mocker): - segment = SegmentFactory.build(type=Segment.TYPE_DYNAMIC, - randomisation_percent=40) + segment = SegmentFactory.build(type=Segment.TYPE_DYNAMIC, randomisation_percent=40) rule = VisitCountRule(counted_page=site.root_page) form = form_with_data(segment, rule) instance = form.save() - mocker.patch('random.randint', return_value=39) + mocker.patch("random.randint", return_value=39) session = client.session session.save() client.get(site.root_page.url) - assert len(client.session['excluded_segments']) == 0 - assert instance.id == client.session['segments'][0]['id'] + assert len(client.session["excluded_segments"]) == 0 + assert instance.id == client.session["segments"][0]["id"] @pytest.mark.django_db -def test_not_offered_dynamic_segment_if_random_is_above_percentage(site, client, mocker): - segment = SegmentFactory.build(type=Segment.TYPE_DYNAMIC, - randomisation_percent=40) +def test_not_offered_dynamic_segment_if_random_is_above_percentage( + site, client, mocker +): + segment = SegmentFactory.build(type=Segment.TYPE_DYNAMIC, randomisation_percent=40) rule = VisitCountRule(counted_page=site.root_page) form = form_with_data(segment, rule) instance = form.save() - mocker.patch('random.randint', return_value=41) + mocker.patch("random.randint", return_value=41) session = client.session session.save() client.get(site.root_page.url) - assert len(client.session['segments']) == 0 - assert instance.id == client.session['excluded_segments'][0]['id'] + assert len(client.session["segments"]) == 0 + assert instance.id == client.session["excluded_segments"][0]["id"] @pytest.mark.django_db def test_not_in_segment_if_percentage_is_0(site, client, mocker, user): - segment = SegmentFactory.build(type=Segment.TYPE_STATIC, count=1, - randomisation_percent=0) + segment = SegmentFactory.build( + type=Segment.TYPE_STATIC, count=1, randomisation_percent=0 + ) rule = VisitCountRule(counted_page=site.root_page) form = form_with_data(segment, rule) instance = form.save() @@ -363,15 +398,16 @@ def test_not_in_segment_if_percentage_is_0(site, client, mocker, user): client.force_login(user) client.get(site.root_page.url) - assert len(client.session['segments']) == 0 + assert len(client.session["segments"]) == 0 assert user not in instance.static_users.all() assert user in instance.excluded_users.all() @pytest.mark.django_db def test_always_in_segment_if_percentage_is_100(site, client, mocker, user): - segment = SegmentFactory.build(type=Segment.TYPE_STATIC, count=1, - randomisation_percent=100) + segment = SegmentFactory.build( + type=Segment.TYPE_STATIC, count=1, randomisation_percent=100 + ) rule = VisitCountRule(counted_page=site.root_page) form = form_with_data(segment, rule) instance = form.save() @@ -381,18 +417,22 @@ def test_always_in_segment_if_percentage_is_100(site, client, mocker, user): client.force_login(user) client.get(site.root_page.url) - assert instance.id == client.session['segments'][0]['id'] + assert instance.id == client.session["segments"][0]["id"] assert user in instance.static_users.all() assert user not in instance.excluded_users.all() @pytest.mark.django_db -def test_not_added_to_static_segment_at_creation_if_random_above_percent(site, mocker, user): - mocker.patch('random.randint', return_value=41) +def test_not_added_to_static_segment_at_creation_if_random_above_percent( + site, mocker, user +): + mocker.patch("random.randint", return_value=41) segment = SegmentFactory.build(type=Segment.TYPE_STATIC, randomisation_percent=40) rule = VisitCountRule(counted_page=site.root_page) form = form_with_data(segment, rule) - mocker.patch('wagtail_personalisation.rules.VisitCountRule.test_user', return_value=True) + mocker.patch( + "wagtail_personalisation.rules.VisitCountRule.test_user", return_value=True + ) instance = form.save() assert user not in instance.static_users.all() @@ -400,12 +440,16 @@ def test_not_added_to_static_segment_at_creation_if_random_above_percent(site, m @pytest.mark.django_db -def test_added_to_static_segment_at_creation_if_random_below_percent(site, mocker, user): - mocker.patch('random.randint', return_value=39) +def test_added_to_static_segment_at_creation_if_random_below_percent( + site, mocker, user +): + mocker.patch("random.randint", return_value=39) segment = SegmentFactory.build(type=Segment.TYPE_STATIC, randomisation_percent=40) rule = VisitCountRule(counted_page=site.root_page) form = form_with_data(segment, rule) - mocker.patch('wagtail_personalisation.rules.VisitCountRule.test_user', return_value=True) + mocker.patch( + "wagtail_personalisation.rules.VisitCountRule.test_user", return_value=True + ) instance = form.save() assert user in instance.static_users.all() @@ -414,8 +458,9 @@ def test_added_to_static_segment_at_creation_if_random_below_percent(site, mocke @pytest.mark.django_db def test_rules_check_skipped_if_user_in_excluded(site, client, mocker, user): - segment = SegmentFactory.build(type=Segment.TYPE_STATIC, count=1, - randomisation_percent=100) + segment = SegmentFactory.build( + type=Segment.TYPE_STATIC, count=1, randomisation_percent=100 + ) rule = VisitCountRule(counted_page=site.root_page) form = form_with_data(segment, rule) instance = form.save() @@ -423,7 +468,8 @@ def test_rules_check_skipped_if_user_in_excluded(site, client, mocker, user): instance.save mock_test_rule = mocker.patch( - 'wagtail_personalisation.adapters.SessionSegmentsAdapter._test_rules') + "wagtail_personalisation.adapters.SessionSegmentsAdapter._test_rules" + ) session = client.session session.save() @@ -431,15 +477,14 @@ def test_rules_check_skipped_if_user_in_excluded(site, client, mocker, user): client.get(site.root_page.url) assert mock_test_rule.call_count == 0 - assert len(client.session['segments']) == 0 + assert len(client.session["segments"]) == 0 assert user not in instance.static_users.all() assert user in instance.excluded_users.all() @pytest.mark.django_db def test_rules_check_skipped_if_dynamic_segment_in_excluded(site, client, mocker, user): - segment = SegmentFactory.build(type=Segment.TYPE_DYNAMIC, - randomisation_percent=100) + segment = SegmentFactory.build(type=Segment.TYPE_DYNAMIC, randomisation_percent=100) rule = VisitCountRule(counted_page=site.root_page) form = form_with_data(segment, rule) instance = form.save() @@ -447,30 +492,35 @@ def test_rules_check_skipped_if_dynamic_segment_in_excluded(site, client, mocker instance.save() session = client.session - session['excluded_segments'] = [{'id': instance.pk}] + session["excluded_segments"] = [{"id": instance.pk}] session.save() mock_test_rule = mocker.patch( - 'wagtail_personalisation.adapters.SessionSegmentsAdapter._test_rules') + "wagtail_personalisation.adapters.SessionSegmentsAdapter._test_rules" + ) client.force_login(user) client.get(site.root_page.url) assert mock_test_rule.call_count == 0 - assert len(client.session['segments']) == 0 + assert len(client.session["segments"]) == 0 @pytest.mark.django_db -def test_matched_user_count_added_to_segment_at_creation(site, mocker, django_user_model): - django_user_model.objects.create(username='first') - django_user_model.objects.create(username='second') +def test_matched_user_count_added_to_segment_at_creation( + site, mocker, django_user_model +): + django_user_model.objects.create(username="first") + django_user_model.objects.create(username="second") segment = SegmentFactory.build(type=Segment.TYPE_STATIC) rule = VisitCountRule() form = form_with_data(segment, rule) form.instance.type = Segment.TYPE_STATIC - mock_test_user = mocker.patch('wagtail_personalisation.rules.VisitCountRule.test_user', return_value=True) + mock_test_user = mocker.patch( + "wagtail_personalisation.rules.VisitCountRule.test_user", return_value=True + ) instance = form.save() assert mock_test_user.call_count == 2 @@ -479,49 +529,59 @@ def test_matched_user_count_added_to_segment_at_creation(site, mocker, django_us @pytest.mark.django_db def test_count_users_matching_static_rules(site, client, mocker, django_user_model): - django_user_model.objects.create(username='first') - django_user_model.objects.create(username='second') + django_user_model.objects.create(username="first") + django_user_model.objects.create(username="second") segment = SegmentFactory.build(type=Segment.TYPE_STATIC) rule = VisitCountRule(counted_page=site.root_page) form = form_with_data(segment, rule) - mocker.patch('wagtail_personalisation.rules.VisitCountRule.test_user', return_value=True) + mocker.patch( + "wagtail_personalisation.rules.VisitCountRule.test_user", return_value=True + ) assert form.count_matching_users([rule], True) == 2 @pytest.mark.django_db def test_count_matching_users_excludes_staff(site, client, mocker, django_user_model): - django_user_model.objects.create(username='first') - django_user_model.objects.create(username='second', is_staff=True) + django_user_model.objects.create(username="first") + django_user_model.objects.create(username="second", is_staff=True) segment = SegmentFactory.build(type=Segment.TYPE_STATIC) rule = VisitCountRule(counted_page=site.root_page) form = form_with_data(segment, rule) - mock_test_user = mocker.patch('wagtail_personalisation.rules.VisitCountRule.test_user', return_value=True) + mock_test_user = mocker.patch( + "wagtail_personalisation.rules.VisitCountRule.test_user", return_value=True + ) assert form.count_matching_users([rule], True) == 1 assert mock_test_user.call_count == 1 @pytest.mark.django_db -def test_count_matching_users_excludes_inactive(site, client, mocker, django_user_model): - django_user_model.objects.create(username='first') - django_user_model.objects.create(username='second', is_active=False) +def test_count_matching_users_excludes_inactive( + site, client, mocker, django_user_model +): + django_user_model.objects.create(username="first") + django_user_model.objects.create(username="second", is_active=False) segment = SegmentFactory.build(type=Segment.TYPE_STATIC) rule = VisitCountRule(counted_page=site.root_page) form = form_with_data(segment, rule) - mock_test_user = mocker.patch('wagtail_personalisation.rules.VisitCountRule.test_user', return_value=True) + mock_test_user = mocker.patch( + "wagtail_personalisation.rules.VisitCountRule.test_user", return_value=True + ) assert form.count_matching_users([rule], True) == 1 assert mock_test_user.call_count == 1 @pytest.mark.django_db -def test_count_matching_users_only_counts_static_rules(site, client, mocker, django_user_model): - django_user_model.objects.create(username='first') - django_user_model.objects.create(username='second') +def test_count_matching_users_only_counts_static_rules( + site, client, mocker, django_user_model +): + django_user_model.objects.create(username="first") + django_user_model.objects.create(username="second") segment = SegmentFactory.build(type=Segment.TYPE_STATIC) rule = TimeRule( @@ -530,16 +590,18 @@ def test_count_matching_users_only_counts_static_rules(site, client, mocker, dja segment=segment, ) form = form_with_data(segment, rule) - mock_test_user = mocker.patch('wagtail_personalisation.rules.TimeRule.test_user') + mock_test_user = mocker.patch("wagtail_personalisation.rules.TimeRule.test_user") assert form.count_matching_users([rule], True) == 0 assert mock_test_user.call_count == 0 @pytest.mark.django_db -def test_count_matching_users_handles_match_any(site, client, mocker, django_user_model): - django_user_model.objects.create(username='first') - django_user_model.objects.create(username='second') +def test_count_matching_users_handles_match_any( + site, client, mocker, django_user_model +): + django_user_model.objects.create(username="first") + django_user_model.objects.create(username="second") segment = SegmentFactory.build(type=Segment.TYPE_STATIC) first_rule = VisitCountRule(counted_page=site.root_page) @@ -548,17 +610,20 @@ def test_count_matching_users_handles_match_any(site, client, mocker, django_use form = form_with_data(segment, first_rule, second_rule) mock_test_user = mocker.patch( - 'wagtail_personalisation.rules.VisitCountRule.test_user', - side_effect=[True, False, True, False]) + "wagtail_personalisation.rules.VisitCountRule.test_user", + side_effect=[True, False, True, False], + ) assert form.count_matching_users([first_rule, second_rule], True) == 2 mock_test_user.call_count == 4 @pytest.mark.django_db -def test_count_matching_users_handles_match_all(site, client, mocker, django_user_model): - django_user_model.objects.create(username='first') - django_user_model.objects.create(username='second') +def test_count_matching_users_handles_match_all( + site, client, mocker, django_user_model +): + django_user_model.objects.create(username="first") + django_user_model.objects.create(username="second") segment = SegmentFactory.build(type=Segment.TYPE_STATIC) first_rule = VisitCountRule(counted_page=site.root_page) @@ -567,8 +632,9 @@ def test_count_matching_users_handles_match_all(site, client, mocker, django_use form = form_with_data(segment, first_rule, second_rule) mock_test_user = mocker.patch( - 'wagtail_personalisation.rules.VisitCountRule.test_user', - side_effect=[True, True, False, True]) + "wagtail_personalisation.rules.VisitCountRule.test_user", + side_effect=[True, True, False, True], + ) assert form.count_matching_users([first_rule, second_rule], False) == 1 mock_test_user.call_count == 4 diff --git a/tests/unit/test_templatetags.py b/tests/unit/test_templatetags.py index 92116bb..d6d4703 100644 --- a/tests/unit/test_templatetags.py +++ b/tests/unit/test_templatetags.py @@ -11,33 +11,44 @@ from tests.utils import render_template @pytest.mark.django_db def test_segment_template_block(rf, site): - SegmentFactory(name='test', persistent=True) + SegmentFactory(name="test", persistent=True) - request = rf.get('/') + request = rf.get("/") - request.session['segments'] = [{ - "encoded_name": 'test', - "id": 1, - "timestamp": int(time.time()), - "persistent": True - }] + request.session["segments"] = [ + { + "encoded_name": "test", + "id": 1, + "timestamp": int(time.time()), + "persistent": True, + } + ] - content = render_template(""" + content = render_template( + """ {% load wagtail_personalisation_tags %} {% segment name='test' %}Test{% endsegment %} - """, request=request).strip() + """, + request=request, + ).strip() assert content == "Test" - content = render_template(""" + content = render_template( + """ {% load wagtail_personalisation_tags %} {% segment name='test2' %}Test{% endsegment %} - """, request=request).strip() + """, + request=request, + ).strip() assert content == "" with pytest.raises(TemplateSyntaxError): - content = render_template(""" + content = render_template( + """ {% load wagtail_personalisation_tags %} {% segment wrongname='test2' %}Test{% endsegment %} - """, request=request).strip() + """, + request=request, + ).strip() diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index 7b2483a..6320afe 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -2,15 +2,19 @@ import pytest from django.test import override_settings from wagtail.core.models import Page as WagtailPage -from tests.factories.page import ( - ContentPageFactory, PersonalisablePageMetadataFactory) +from tests.factories.page import ContentPageFactory, PersonalisablePageMetadataFactory from wagtail_personalisation.utils import ( - can_delete_pages, exclude_variants, get_client_ip, impersonate_other_page) + can_delete_pages, + exclude_variants, + get_client_ip, + impersonate_other_page, +) locale_factory = False try: from tests.factories.page import LocaleFactory # noqa + locale_factory = True # noqa except ImportError: pass @@ -18,23 +22,23 @@ except ImportError: @pytest.fixture def rootpage(): - return ContentPageFactory(parent=None, path='/', depth=0, title='root') + return ContentPageFactory(parent=None, path="/", depth=0, title="root") @pytest.fixture def page(rootpage): - return ContentPageFactory(parent=rootpage, path='/hi', title='Hi') + return ContentPageFactory(parent=rootpage, path="/hi", title="Hi") @pytest.fixture def otherpage(rootpage): - return ContentPageFactory(parent=rootpage, path='/bye', title='Bye') + return ContentPageFactory(parent=rootpage, path="/bye", title="Bye") @pytest.mark.django_db def test_impersonate_other_page(page, otherpage): impersonate_other_page(page, otherpage) - assert page.title == otherpage.title == 'Bye' + assert page.title == otherpage.title == "Bye" assert page.path == otherpage.path @@ -50,58 +54,63 @@ def test_cannot_delete_pages_with_standard_user(user, segmented_page): def test_get_client_ip_with_remote_addr(rf): - request = rf.get('/', REMOTE_ADDR='173.231.235.87') - assert get_client_ip(request) == '173.231.235.87' + request = rf.get("/", REMOTE_ADDR="173.231.235.87") + assert get_client_ip(request) == "173.231.235.87" def test_get_client_ip_with_x_forwarded_for(rf): - request = rf.get('/', HTTP_X_FORWARDED_FOR='173.231.235.87', - REMOTE_ADDR='10.0.23.24') - assert get_client_ip(request) == '173.231.235.87' + request = rf.get( + "/", HTTP_X_FORWARDED_FOR="173.231.235.87", REMOTE_ADDR="10.0.23.24" + ) + assert get_client_ip(request) == "173.231.235.87" -@override_settings( - WAGTAIL_PERSONALISATION_IP_FUNCTION='some.non.existent.path' -) +@override_settings(WAGTAIL_PERSONALISATION_IP_FUNCTION="some.non.existent.path") def test_get_client_ip_custom_get_client_ip_function_does_not_exist(rf): with pytest.raises(ImportError): - get_client_ip(rf.get('/')) + get_client_ip(rf.get("/")) -@override_settings( - WAGTAIL_PERSONALISATION_IP_FUNCTION='tests.utils.get_custom_ip' -) +@override_settings(WAGTAIL_PERSONALISATION_IP_FUNCTION="tests.utils.get_custom_ip") def test_get_client_ip_custom_get_client_ip_used(rf): - assert get_client_ip(rf.get('/')) == '123.123.123.123' + 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 = ContentPageFactory( + path="/" + str(i), depth=0, url_path="/", title="Hoi " + str(i) + ) page.save() - pages = WagtailPage.objects.all().specific().order_by('id') + 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)) + 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 = ContentPageFactory( + path="/" + str(i), depth=0, url_path="/", title="Hoi " + str(i) + ) page.save() - pages = WagtailPage.objects.all().specific().order_by('id') + 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) + variant = ContentPageFactory(title="variant %d" % page.pk) + page.personalisation_metadata = PersonalisablePageMetadataFactory( + canonical_page=page, variant=variant + ) page.save() pages = WagtailPage.objects.all().specific() @@ -111,19 +120,25 @@ def test_exclude_variants_with_pages_querysets_not_canonical(): 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 = ContentPageFactory( + path="/" + str(i), depth=0, url_path="/", title="Hoi " + str(i) + ) page.save() - pages = WagtailPage.objects.all().specific().order_by('id') + pages = WagtailPage.objects.all().specific().order_by("id") # add variants for page in pages: - page.personalisation_metadata = PersonalisablePageMetadataFactory(canonical_page=page, variant=page) + 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)) + assert set(result.values_list("pk", flat=True)) == set( + pages.values_list("pk", flat=True) + ) diff --git a/tests/unit/test_views.py b/tests/unit/test_views.py index bd8d302..a263b85 100644 --- a/tests/unit/test_views.py +++ b/tests/unit/test_views.py @@ -5,31 +5,29 @@ 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) +from wagtail_personalisation.views import SegmentModelAdmin, SegmentModelDeleteView @pytest.mark.django_db def test_segment_user_data_view_requires_admin_access(site, client, django_user_model): - user = django_user_model.objects.create(username='first') + user = django_user_model.objects.create(username="first") segment = Segment(type=Segment.TYPE_STATIC, count=1) segment.save() client.force_login(user) - url = reverse('segment:segment_user_data', args=(segment.id,)) + url = reverse("segment:segment_user_data", args=(segment.id,)) response = client.get(url) assert response.status_code == 302 - assert response.url == '/admin/login/?next=%s' % url + assert response.url == "/admin/login/?next=%s" % url @pytest.mark.django_db def test_segment_user_data_view(site, client, mocker, django_user_model): - user1 = django_user_model.objects.create(username='first') - user2 = django_user_model.objects.create(username='second') - admin_user = django_user_model.objects.create( - username='admin', is_superuser=True) + user1 = django_user_model.objects.create(username="first") + user2 = django_user_model.objects.create(username="second") + admin_user = django_user_model.objects.create(username="admin", is_superuser=True) segment = Segment(type=Segment.TYPE_STATIC, count=1) segment.save() @@ -37,24 +35,28 @@ def test_segment_user_data_view(site, client, mocker, django_user_model): segment.static_users.add(user2) rule1 = VisitCountRule(counted_page=site.root_page, segment=segment) - rule2 = VisitCountRule(counted_page=site.root_page.get_last_child(), - segment=segment) + rule2 = VisitCountRule( + counted_page=site.root_page.get_last_child(), segment=segment + ) rule1.save() rule2.save() - mocker.patch('wagtail_personalisation.rules.VisitCountRule.get_user_info_string', - side_effect=[3, 9, 0, 1]) + mocker.patch( + "wagtail_personalisation.rules.VisitCountRule.get_user_info_string", + side_effect=[3, 9, 0, 1], + ) client.force_login(admin_user) - response = client.get( - reverse('segment:segment_user_data', args=(segment.id,))) + response = client.get(reverse("segment:segment_user_data", args=(segment.id,))) assert response.status_code == 200 data_lines = response.content.decode().split("\n") - assert data_lines[0] == 'Username,Visit count - Test page,Visit count - Regular page\r' - assert data_lines[1] == 'first,3,9\r' - assert data_lines[2] == 'second,0,1\r' + assert ( + data_lines[0] == "Username,Visit count - Test page,Visit count - Regular page\r" + ) + assert data_lines[1] == "first,3,9\r" + assert data_lines[2] == "second,0,1\r" @pytest.mark.django_db @@ -64,9 +66,9 @@ def test_segment_delete_view_delete_instance(rf, segmented_page, user): segment = segmented_page.personalisation_metadata.segment canonical_page = segmented_page.personalisation_metadata.canonical_page variants_metadata = segment.get_used_pages() - page_variants = Page.objects.filter(pk__in=( - variants_metadata.values_list('variant_id', flat=True) - )) + page_variants = Page.objects.filter( + pk__in=(variants_metadata.values_list("variant_id", flat=True)) + ) # Make sure all canonical page, variants and variants metadata exist assert canonical_page @@ -74,11 +76,10 @@ def test_segment_delete_view_delete_instance(rf, segmented_page, user): assert variants_metadata # Delete the segment via the method on the view. - request = rf.get('/'.format(segment.pk)) # noqa + request = rf.get("/".format(segment.pk)) # noqa request.user = user view = SegmentModelDeleteView( - instance_pk=str(segment.pk), - model_admin=SegmentModelAdmin() + instance_pk=str(segment.pk), model_admin=SegmentModelAdmin() ) view.request = request view.delete_instance() @@ -98,13 +99,12 @@ def test_segment_delete_view_delete_instance(rf, segmented_page, user): @pytest.mark.django_db def test_segment_delete_view_raises_permission_denied(rf, segmented_page, user): segment = segmented_page.personalisation_metadata.segment - request = rf.get('/'.format(segment.pk)) # noqa + request = rf.get("/".format(segment.pk)) # noqa request.user = user view = SegmentModelDeleteView( - instance_pk=str(segment.pk), - model_admin=SegmentModelAdmin() + instance_pk=str(segment.pk), model_admin=SegmentModelAdmin() ) view.request = request - message = 'User have no permission to delete variant page objects.' # noqa + message = "User have no permission to delete variant page objects." # noqa with pytest.raises(PermissionDenied): view.delete_instance() diff --git a/tests/unit/test_wagtail_hooks.py b/tests/unit/test_wagtail_hooks.py index 917ac2f..676098f 100644 --- a/tests/unit/test_wagtail_hooks.py +++ b/tests/unit/test_wagtail_hooks.py @@ -10,7 +10,7 @@ from wagtail_personalisation import adapters, wagtail_hooks @pytest.mark.django_db def test_serve_variant_no_variant(site, rf): page = site.root_page - request = rf.get('/') + request = rf.get("/") args = tuple() kwargs = {} @@ -20,7 +20,7 @@ def test_serve_variant_no_variant(site, rf): @pytest.mark.django_db def test_variant_accessed_directly_returns_404(segmented_page, rf): - request = rf.get('/') + request = rf.get("/") args = tuple() kwargs = {} with pytest.raises(Http404): @@ -29,7 +29,7 @@ def test_variant_accessed_directly_returns_404(segmented_page, rf): @pytest.mark.django_db def test_serve_variant_with_variant_no_segment(site, rf, segmented_page): - request = rf.get('/') + request = rf.get("/") args = tuple() kwargs = {} @@ -40,7 +40,7 @@ def test_serve_variant_with_variant_no_segment(site, rf, segmented_page): @pytest.mark.django_db def test_serve_variant_with_variant_segmented(site, rf, segmented_page): - request = rf.get('/') + request = rf.get("/") args = tuple() kwargs = {} @@ -58,7 +58,7 @@ def test_serve_variant_with_variant_segmented(site, rf, segmented_page): def test_page_listing_variant_buttons(site, rf, segmented_page): page = segmented_page.personalisation_metadata.canonical_page - SegmentFactory(name='something') + SegmentFactory(name="something") result = wagtail_hooks.page_listing_variant_buttons(page, []) items = list(result) assert len(items) == 1 @@ -68,47 +68,42 @@ def test_page_listing_variant_buttons(site, rf, segmented_page): def test_page_listing_more_buttons(site, rf, segmented_page): page = segmented_page.personalisation_metadata.canonical_page - SegmentFactory(name='something') + SegmentFactory(name="something") result = wagtail_hooks.page_listing_more_buttons(page, []) items = list(result) assert len(items) == 3 @pytest.mark.django_db -def test_custom_delete_page_view_does_not_trigger_for_variants( - rf, - segmented_page -): - assert ( - wagtail_hooks.delete_related_variants(rf.get('/'), segmented_page) - ) is None +def test_custom_delete_page_view_does_not_trigger_for_variants(rf, segmented_page): + assert (wagtail_hooks.delete_related_variants(rf.get("/"), segmented_page)) is None @pytest.mark.django_db -def test_custom_delete_page_view_triggers_for_canonical_pages( - rf, - segmented_page -): +def test_custom_delete_page_view_triggers_for_canonical_pages(rf, segmented_page): assert ( wagtail_hooks.delete_related_variants( - rf.get('/'), - segmented_page.personalisation_metadata.canonical_page + rf.get("/"), segmented_page.personalisation_metadata.canonical_page ) ) is not None @pytest.mark.django_db def test_custom_delete_page_view_deletes_variants(rf, segmented_page, user): - post_request = rf.post('/') + post_request = rf.post("/") user.is_superuser = True rf.user = user canonical_page = segmented_page.personalisation_metadata.canonical_page canonical_page_variant = canonical_page.personalisation_metadata assert canonical_page_variant - variants = Page.objects.filter(pk__in=( - canonical_page.personalisation_metadata.variants_metadata.values_list('variant_id', flat=True) - )) + variants = Page.objects.filter( + pk__in=( + canonical_page.personalisation_metadata.variants_metadata.values_list( + "variant_id", flat=True + ) + ) + ) variants_metadata = canonical_page.personalisation_metadata.variants_metadata # Make sure there are variants that exist in the database. assert len(variants.all()) @@ -126,18 +121,20 @@ def test_custom_delete_page_view_deletes_variants(rf, segmented_page, user): @pytest.mark.django_db -def test_custom_delete_page_view_deletes_variants_of_child_pages(rf, segmented_page, user): +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('/') + 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 + 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) diff --git a/tests/utils.py b/tests/utils.py index e1a5e49..77b5458 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -2,10 +2,10 @@ from django.template import engines def render_template(value, **context): - template = engines['django'].from_string(value) - request = context.pop('request', None) + template = engines["django"].from_string(value) + request = context.pop("request", None) return template.render(context, request) def get_custom_ip(request): - return '123.123.123.123' + return "123.123.123.123"