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 beb0290..d627f2b 100644 --- a/src/personalisation/middleware.py +++ b/src/personalisation/middleware.py @@ -1,4 +1,5 @@ -from personalisation.models import Segment, AbstractBaseRule, TimeRule +from personalisation.models import AbstractBaseRule, Segment + class SegmentMiddleware(object): """Middleware for testing and putting a user in a segment""" @@ -12,10 +13,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, request) + if result: chosen_segments.append(segment.encoded_name()) @@ -25,3 +25,13 @@ class SegmentMiddleware(object): print(request.session['segments']) return response + + + def test_rules(self, rules, request): + for rule in rules: + result = rule.test_user(request) + + if result is False: + return False + + return True 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 new file mode 100644 index 0000000..fa602d3 --- /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 + +import django.db.models.deletion +from django.db import migrations, models + + +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 2ac3b00..316b7f2 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, time +import re +from datetime import datetime -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 _ - -from modelcluster.models import ClusterableModel from model_utils.managers import InheritanceManager +from modelcluster.models import ClusterableModel from wagtail.wagtailadmin.edit_handlers import FieldPanel @@ -36,7 +35,6 @@ class Segment(ClusterableModel): return "".join(self.name.lower().split()) - """ Base for creating rules to segment users with """ @@ -57,7 +55,7 @@ class AbstractBaseRule(models.Model): """ -Time rule to segment users with +Time rule to segment users based on a start and end time """ @python_2_unicode_compatible class TimeRule(AbstractBaseRule): @@ -67,9 +65,32 @@ class TimeRule(AbstractBaseRule): def __init__(self, *args, **kwargs): super(TimeRule, self).__init__(*args, **kwargs) - def test_user(self, request=None): - current_time = datetime.now().time() + def test_user(self, request): + 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 +""" +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(self.regex_string) + + if 'HTTP_REFERER' in request.META: + referer = request.META['HTTP_REFERER'] + if pattern.search(referer): + return True + return False 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