From 40a9c598bb7d5f46e07512a76db250f7c7e43011 Mon Sep 17 00:00:00 2001 From: Boris Besemer Date: Mon, 7 Nov 2016 17:33:44 +0100 Subject: [PATCH 1/4] adds referral rule --- src/personalisation/models.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/personalisation/models.py b/src/personalisation/models.py index 2ac3b00..dc6f54e 100644 --- a/src/personalisation/models.py +++ b/src/personalisation/models.py @@ -1,6 +1,7 @@ from __future__ import absolute_import, unicode_literals from datetime import datetime, time +import re from django.conf import settings from django.db import models @@ -57,7 +58,7 @@ class AbstractBaseRule(models.Model): """ -Time rule to segment users with +Time rule to segment users based on the """ @python_2_unicode_compatible class TimeRule(AbstractBaseRule): @@ -67,9 +68,23 @@ class TimeRule(AbstractBaseRule): def __init__(self, *args, **kwargs): super(TimeRule, self).__init__(*args, **kwargs) - def test_user(self, request=None): + def test_user(self): current_time = datetime.now().time() starting_time = self.start_time ending_time = self.end_time return starting_time <= current_time <= ending_time + + +""" +Referral rule to segment users based on a regex test +""" +class ReferralRule(AbstractBaseRule): + regex_string = models.TextField() + + def __init__(self, *args, **kwargs): + super(ReferralRule, self).__init__(*args, **kwargs) + + def test_user(self, request): + pattern = re.compile(re.escape(r'{0}').format(self.regex_string)) + return pattern.match(request.META.HTTP_REFERER) From 5e06baeee2862d6de7c284f8ca89dd54dde8ad26 Mon Sep 17 00:00:00 2001 From: Boris Besemer Date: Tue, 8 Nov 2016 09:04:57 +0100 Subject: [PATCH 2/4] adds referral rule and fixes segment testing --- src/personalisation/middleware.py | 17 +++++++++---- .../migrations/0005_referralrule.py | 24 +++++++++++++++++++ src/personalisation/models.py | 6 ++--- 3 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 src/personalisation/migrations/0005_referralrule.py diff --git a/src/personalisation/middleware.py b/src/personalisation/middleware.py index beb0290..611ea4d 100644 --- a/src/personalisation/middleware.py +++ b/src/personalisation/middleware.py @@ -1,4 +1,4 @@ -from personalisation.models import Segment, AbstractBaseRule, TimeRule +from personalisation.models import Segment, AbstractBaseRule class SegmentMiddleware(object): """Middleware for testing and putting a user in a segment""" @@ -12,10 +12,9 @@ class SegmentMiddleware(object): chosen_segments = [] for segment in segments: - result = False rules = AbstractBaseRule.objects.filter(segment=segment).select_subclasses() - for rule in rules: - result = rule.test_user() + result = self.test_rules(rules) + if result: chosen_segments.append(segment.encoded_name()) @@ -25,3 +24,13 @@ class SegmentMiddleware(object): print(request.session['segments']) return response + + + def test_rules(self, rules): + for rule in rules: + result = rule.test_user() + + if result is False: + return False + + return True diff --git a/src/personalisation/migrations/0005_referralrule.py b/src/personalisation/migrations/0005_referralrule.py new file mode 100644 index 0000000..2d9eb9b --- /dev/null +++ b/src/personalisation/migrations/0005_referralrule.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2016-11-08 07:47 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('personalisation', '0004_segment_status'), + ] + + operations = [ + migrations.CreateModel( + name='ReferralRule', + fields=[ + ('abstractbaserule_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='personalisation.AbstractBaseRule')), + ('regex_string', models.TextField()), + ], + bases=('personalisation.abstractbaserule',), + ), + ] diff --git a/src/personalisation/models.py b/src/personalisation/models.py index dc6f54e..c918c1f 100644 --- a/src/personalisation/models.py +++ b/src/personalisation/models.py @@ -1,9 +1,8 @@ from __future__ import absolute_import, unicode_literals -from datetime import datetime, time +from datetime import datetime import re -from django.conf import settings from django.db import models from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ @@ -37,7 +36,6 @@ class Segment(ClusterableModel): return "".join(self.name.lower().split()) - """ Base for creating rules to segment users with """ @@ -58,7 +56,7 @@ class AbstractBaseRule(models.Model): """ -Time rule to segment users based on the +Time rule to segment users based on a start and end time """ @python_2_unicode_compatible class TimeRule(AbstractBaseRule): From 9956d5595af34994632cd0b1abec4e94cd91a548 Mon Sep 17 00:00:00 2001 From: Boris Besemer Date: Tue, 8 Nov 2016 10:52:06 +0100 Subject: [PATCH 3/4] adds referral to the admin and isorts imports --- Makefile | 2 +- src/personalisation/admin.py | 6 +++++- src/personalisation/admin_urls.py | 3 ++- src/personalisation/middleware.py | 3 ++- .../migrations/0002_abstractbaserule_timerule.py | 2 +- .../migrations/0003_abstractbaserule_segment.py | 2 +- src/personalisation/migrations/0005_referralrule.py | 2 +- src/personalisation/models.py | 11 +++++++---- src/personalisation/views.py | 2 +- src/personalisation/wagtail_hooks.py | 7 +++---- tests/test_models.py | 12 ++++++++++++ 11 files changed, 36 insertions(+), 16 deletions(-) create mode 100644 tests/test_models.py diff --git a/Makefile b/Makefile index ceb35c6..6100cd1 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ flake8: isort: pip install isort - isort --recursive --check-only --diff src tests + isort --recursive src tests dist: diff --git a/src/personalisation/admin.py b/src/personalisation/admin.py index c111dea..856aea2 100644 --- a/src/personalisation/admin.py +++ b/src/personalisation/admin.py @@ -2,15 +2,19 @@ from django.contrib import admin from personalisation import models + class TimeRuleAdmin(admin.ModelAdmin): list_display = ('name', 'start_time', 'end_time') class TimeRuleAdminInline(admin.TabularInline): model = models.TimeRule +class ReferralRuleAdminInline(admin.TabularInline): + model = models.ReferralRule + class SegmentAdmin(admin.ModelAdmin): list_display = ['name'] - inlines = (TimeRuleAdminInline,) + inlines = (TimeRuleAdminInline, ReferralRuleAdminInline) admin.site.register(models.TimeRule, TimeRuleAdmin) diff --git a/src/personalisation/admin_urls.py b/src/personalisation/admin_urls.py index 2e6e5e6..ccf42b8 100644 --- a/src/personalisation/admin_urls.py +++ b/src/personalisation/admin_urls.py @@ -1,8 +1,9 @@ from __future__ import absolute_import, unicode_literals from django.conf.urls import url + from personalisation import views urlpatterns = [ url(r'^segment/(\d+)/$', views.overview, name='overview'), -] \ No newline at end of file +] diff --git a/src/personalisation/middleware.py b/src/personalisation/middleware.py index 611ea4d..1f86fc0 100644 --- a/src/personalisation/middleware.py +++ b/src/personalisation/middleware.py @@ -1,4 +1,5 @@ -from personalisation.models import Segment, AbstractBaseRule +from personalisation.models import AbstractBaseRule, Segment + class SegmentMiddleware(object): """Middleware for testing and putting a user in a segment""" diff --git a/src/personalisation/migrations/0002_abstractbaserule_timerule.py b/src/personalisation/migrations/0002_abstractbaserule_timerule.py index 2cae5d2..7a31721 100644 --- a/src/personalisation/migrations/0002_abstractbaserule_timerule.py +++ b/src/personalisation/migrations/0002_abstractbaserule_timerule.py @@ -2,8 +2,8 @@ # Generated by Django 1.10.3 on 2016-11-07 13:53 from __future__ import unicode_literals -from django.db import migrations, models import django.db.models.deletion +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/src/personalisation/migrations/0003_abstractbaserule_segment.py b/src/personalisation/migrations/0003_abstractbaserule_segment.py index d2ac8b4..2979468 100644 --- a/src/personalisation/migrations/0003_abstractbaserule_segment.py +++ b/src/personalisation/migrations/0003_abstractbaserule_segment.py @@ -2,8 +2,8 @@ # Generated by Django 1.10.3 on 2016-11-07 14:12 from __future__ import unicode_literals -from django.db import migrations, models import django.db.models.deletion +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/src/personalisation/migrations/0005_referralrule.py b/src/personalisation/migrations/0005_referralrule.py index 2d9eb9b..fa602d3 100644 --- a/src/personalisation/migrations/0005_referralrule.py +++ b/src/personalisation/migrations/0005_referralrule.py @@ -2,8 +2,8 @@ # Generated by Django 1.10.3 on 2016-11-08 07:47 from __future__ import unicode_literals -from django.db import migrations, models import django.db.models.deletion +from django.db import migrations, models class Migration(migrations.Migration): diff --git a/src/personalisation/models.py b/src/personalisation/models.py index c918c1f..c763295 100644 --- a/src/personalisation/models.py +++ b/src/personalisation/models.py @@ -1,14 +1,13 @@ from __future__ import absolute_import, unicode_literals -from datetime import datetime import re +from datetime import datetime from django.db import models from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ - -from modelcluster.models import ClusterableModel from model_utils.managers import InheritanceManager +from modelcluster.models import ClusterableModel from wagtail.wagtailadmin.edit_handlers import FieldPanel @@ -67,12 +66,16 @@ class TimeRule(AbstractBaseRule): super(TimeRule, self).__init__(*args, **kwargs) def test_user(self): - current_time = datetime.now().time() + current_time = self.get_current_time() starting_time = self.start_time ending_time = self.end_time return starting_time <= current_time <= ending_time + def get_current_time(self): + """Mockable function for testing purposes""" + return datetime.now().time() + """ Referral rule to segment users based on a regex test diff --git a/src/personalisation/views.py b/src/personalisation/views.py index 658cf00..db8e3a3 100644 --- a/src/personalisation/views.py +++ b/src/personalisation/views.py @@ -7,4 +7,4 @@ from django.shortcuts import render Segments overview """ def overview(request): - return render(request, 'wagtailadmin/segment.html') \ No newline at end of file + return render(request, 'wagtailadmin/segment.html') diff --git a/src/personalisation/wagtail_hooks.py b/src/personalisation/wagtail_hooks.py index 39fc91a..a96f139 100644 --- a/src/personalisation/wagtail_hooks.py +++ b/src/personalisation/wagtail_hooks.py @@ -1,14 +1,13 @@ from django.conf import settings from django.conf.urls import include, url from django.core.urlresolvers import reverse -from wagtail.wagtailadmin import widgets -from wagtail.wagtailadmin.menu import MenuItem - -from personalisation import admin_urls from wagtail.contrib.modeladmin.helpers import ButtonHelper from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register +from wagtail.wagtailadmin import widgets +from wagtail.wagtailadmin.menu import MenuItem from wagtail.wagtailcore import hooks +from personalisation import admin_urls from personalisation.models import Segment diff --git a/tests/test_models.py b/tests/test_models.py new file mode 100644 index 0000000..de39efc --- /dev/null +++ b/tests/test_models.py @@ -0,0 +1,12 @@ +import datetime + +import pytest +from personalisation.models import TimeRule + + +@pytest.mark.django_db +def test_create_time_rule(): + time_rule = TimeRule(name='test', start_time="08:00:00", end_time="23:00:00") + + with mock.patch('TimeRule.get_current_time', return_value=datetime.time(10, 00, 00)): + assert time_rule.test_user() is True From 253a53433b5ee4a2b93a56259751e6d91bc286b1 Mon Sep 17 00:00:00 2001 From: Boris Besemer Date: Tue, 8 Nov 2016 11:18:41 +0100 Subject: [PATCH 4/4] adds matching functionality to referral rule --- src/personalisation/middleware.py | 6 +++--- src/personalisation/models.py | 11 ++++++++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/personalisation/middleware.py b/src/personalisation/middleware.py index 1f86fc0..d627f2b 100644 --- a/src/personalisation/middleware.py +++ b/src/personalisation/middleware.py @@ -14,7 +14,7 @@ class SegmentMiddleware(object): for segment in segments: rules = AbstractBaseRule.objects.filter(segment=segment).select_subclasses() - result = self.test_rules(rules) + result = self.test_rules(rules, request) if result: chosen_segments.append(segment.encoded_name()) @@ -27,9 +27,9 @@ class SegmentMiddleware(object): return response - def test_rules(self, rules): + def test_rules(self, rules, request): for rule in rules: - result = rule.test_user() + result = rule.test_user(request) if result is False: return False diff --git a/src/personalisation/models.py b/src/personalisation/models.py index c763295..316b7f2 100644 --- a/src/personalisation/models.py +++ b/src/personalisation/models.py @@ -65,7 +65,7 @@ class TimeRule(AbstractBaseRule): def __init__(self, *args, **kwargs): super(TimeRule, self).__init__(*args, **kwargs) - def test_user(self): + def test_user(self, request): current_time = self.get_current_time() starting_time = self.start_time ending_time = self.end_time @@ -87,5 +87,10 @@ class ReferralRule(AbstractBaseRule): super(ReferralRule, self).__init__(*args, **kwargs) def test_user(self, request): - pattern = re.compile(re.escape(r'{0}').format(self.regex_string)) - return pattern.match(request.META.HTTP_REFERER) + pattern = re.compile(self.regex_string) + + if 'HTTP_REFERER' in request.META: + referer = request.META['HTTP_REFERER'] + if pattern.search(referer): + return True + return False