From 29b9ad695b5c3227e55e1217de541113cb2f86ee Mon Sep 17 00:00:00 2001 From: Boris Besemer Date: Tue, 6 Dec 2016 12:17:50 +0100 Subject: [PATCH] moves all middleware logic to wagtail hook --- README.rst | 10 ---- src/personalisation/middleware.py | 89 ---------------------------- src/personalisation/wagtail_hooks.py | 86 ++++++++++++++++++++++++--- tests/sandbox/settings/base.py | 1 - tests/unit/test_middleware.py | 12 ---- 5 files changed, 78 insertions(+), 120 deletions(-) delete mode 100644 src/personalisation/middleware.py diff --git a/README.rst b/README.rst index e9532a8..3105f3c 100644 --- a/README.rst +++ b/README.rst @@ -21,14 +21,4 @@ Next, include the ``personalisation`` and ``wagtail.contrib.modeladmin`` app in # ... ] -Last but not least, add ``personalisation.middleware.SegmentMiddleware`` to your project's ``MIDDLEWARE``: - -.. code-block:: python - - MIDDLEWARE = [ - # ... - 'personalisation.middleware.SegmentMiddleware', - # ... - ] - Make sure that ``django.contrib.sessions.middleware.SessionMiddleware`` has been added in first, this is a prerequisite for this project. diff --git a/src/personalisation/middleware.py b/src/personalisation/middleware.py deleted file mode 100644 index c1783b9..0000000 --- a/src/personalisation/middleware.py +++ /dev/null @@ -1,89 +0,0 @@ -import logging -import time - -from django.urls import reverse - -from personalisation.models import AbstractBaseRule, Segment - -logger = logging.getLogger() - - -class SegmentMiddleware(object): - """Middleware for testing and putting a user in a segment""" - - def __init__(self, get_response=None): - self.get_response = get_response - - def __call__(self, request): - reverse_urls = [reverse('admin:index'), reverse('wagtailadmin_home')] - - if 'visit_count' not in request.session: - request.session['visit_count'] = [] - - request.session['segments'] = [] - - if any(request.path.startswith(item) for item in reverse_urls): - return self.get_response(request) - - segments = Segment.objects.all().filter(status='enabled') - - for segment in segments: - rules = AbstractBaseRule.__subclasses__() - segment_rules = [] - for rule in rules: - queried_rules = rule.objects.filter(segment=segment) - for result in queried_rules: - segment_rules.append(result) - result = self.test_rules(segment_rules, request) - - if result: - self.add_segment_to_user(segment, request) - - response = self.get_response(request) - - if request.session['segments']: - logger.info("User has been added to the following segments: {}".format(request.session['segments'])) - - return response - - def test_rules(self, rules, request): - """Test wether the user matches a segment's rules'""" - if len(rules) > 0: - for rule in rules: - result = rule.test_user(request) - - # Debug - if result and rule.__class__.__name__ == "TimeRule": - print("User segmented. Time between {} and {}.".format( - rule.start_time, - rule.end_time)) - if result and rule.__class__.__name__ == "ReferralRule": - print("User segmented. Referral matches {}.".format(rule.regex_string)) - if result and rule.__class__.__name__ == "VisitCountRule": - print("User segmented. Visited {} {} {} times.".format( - rule.counted_page, - rule.operator, - rule.count)) - - if result is False: - return False - - return True - return False - - def add_segment_to_user(self, segment, request): - """Save the segment in the user session""" - def check_if_segmented(segment): - """Check if the user has been segmented""" - for seg in request.session['segments']: - if seg['encoded_name'] == segment.encoded_name(): - return True - return False - - if not check_if_segmented(segment): - segdict = { - "encoded_name": segment.encoded_name(), - "id": segment.pk, - "timestamp": int(time.time()), - } - request.session['segments'].append(segdict) diff --git a/src/personalisation/wagtail_hooks.py b/src/personalisation/wagtail_hooks.py index ecc9150..a737448 100644 --- a/src/personalisation/wagtail_hooks.py +++ b/src/personalisation/wagtail_hooks.py @@ -1,14 +1,18 @@ import time +import logging + +from django.shortcuts import reverse from django.conf.urls import include, url from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register from wagtail.contrib.modeladmin.views import IndexView from wagtail.wagtailcore import hooks from personalisation import admin_urls -from personalisation.models import PersonalisablePage, Segment +from personalisation.models import AbstractBaseRule, PersonalisablePage, Segment from personalisation.utils import impersonate_other_page +logger = logging.getLogger() @hooks.register('register_admin_urls') def register_admin_urls(): @@ -31,17 +35,13 @@ class SegmentModelAdmin(ModelAdmin): form_view_extra_css = ['personalisation/segment/form.css'] inspect_view_enabled = True -modeladmin_register(SegmentModelAdmin) +modeladmin_register(SegmentModelAdmin) @hooks.register('before_serve_page') def set_visit_count(page, request, serve_args, serve_kwargs): - """Update the users visit count before each page visit.""" - # Update the segment visit count - for seg in request.session['segments']: - segment = Segment.objects.get(pk=seg['id']) - segment.visit_count = segment.visit_count + 1 - segment.save() + if 'visit_count' not in request.session: + request.session['visit_count'] = [] # Update the page visit count def create_new_counter(page, request): @@ -68,6 +68,76 @@ def set_visit_count(page, request, serve_args, serve_kwargs): # No counters exist. Create a new counter with count value 1. create_new_counter(page, request) +@hooks.register('before_serve_page') +def segment_user(page, request, serve_args, serve_kwargs): + request.session['segments'] = [] + segments = Segment.objects.all().filter(status='enabled') + + for segment in segments: + rules = AbstractBaseRule.__subclasses__() + segment_rules = [] + for rule in rules: + queried_rules = rule.objects.filter(segment=segment) + for result in queried_rules: + segment_rules.append(result) + result = _test_rules(segment_rules, request) + + if result: + _add_segment_to_user(segment, request) + + if request.session['segments']: + logger.info("User has been added to the following segments: {}".format(request.session['segments'])) + + for seg in request.session['segments']: + segment = Segment.objects.get(pk=seg['id']) + segment.visit_count = segment.visit_count + 1 + segment.save() + + +def _test_rules(rules, request): + """Test wether the user matches a segment's rules'""" + if len(rules) > 0: + for rule in rules: + result = rule.test_user(request) + + # Debug + if result and rule.__class__.__name__ == "TimeRule": + print("User segmented. Time between {} and {}.".format( + rule.start_time, + rule.end_time)) + if result and rule.__class__.__name__ == "ReferralRule": + print("User segmented. Referral matches {}.".format(rule.regex_string)) + if result and rule.__class__.__name__ == "VisitCountRule": + print("User segmented. Visited {} {} {} times.".format( + rule.counted_page, + rule.operator, + rule.count)) + + if result is False: + return False + + return True + return False + + +def _add_segment_to_user(segment, request): + """Save the segment in the user session""" + + def check_if_segmented(segment): + """Check if the user has been segmented""" + for seg in request.session['segments']: + if seg['encoded_name'] == segment.encoded_name(): + return True + return False + + if not check_if_segmented(segment): + segdict = { + "encoded_name": segment.encoded_name(), + "id": segment.pk, + "timestamp": int(time.time()), + } + request.session['segments'].append(segdict) + @hooks.register('before_serve_page') def serve_variation(page, request, serve_args, serve_kwargs): user_segments = [] diff --git a/tests/sandbox/settings/base.py b/tests/sandbox/settings/base.py index 7d158d4..43fade8 100644 --- a/tests/sandbox/settings/base.py +++ b/tests/sandbox/settings/base.py @@ -52,7 +52,6 @@ MIDDLEWARE = ( 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'wagtail.wagtailcore.middleware.SiteMiddleware', - 'personalisation.middleware.SegmentMiddleware', ) INSTALLED_APPS = ( diff --git a/tests/unit/test_middleware.py b/tests/unit/test_middleware.py index bbe309d..1eaee9e 100644 --- a/tests/unit/test_middleware.py +++ b/tests/unit/test_middleware.py @@ -195,15 +195,3 @@ class TestUserVisitCount(object): client.get('/') assert not any(item['path'] == '/doesntexist' for item in client.session['visit_count']) - - - def test_ignores_admin_visits(self, client): - client.get('/admin/') - - assert not any(item['path'] == '/admin/' for item in client.session['visit_count']) - - client.get('/django-admin/') - - assert not any(item['path'] == '/django-admin/' for item in client.session['visit_count']) - - assert client.session['visit_count'] == []