8

Merge pull request #23 from LabD/feature/segments-adapter

Feature/segments adapter
This commit is contained in:
Boris Besemer
2016-12-28 12:25:49 +01:00
committed by GitHub
4 changed files with 120 additions and 68 deletions

View File

@@ -23,3 +23,14 @@ Next, include the ``personalisation`` and ``wagtail.contrib.modeladmin`` app in
]
Make sure that ``django.contrib.sessions.middleware.SessionMiddleware`` has been added in first, this is a prerequisite for this project.
Changing segments adapter
-------------------------
To change the segments adapter, first make a new one based on the ``BaseSegmentsAdapter``
.. code-block:: python
class YourSegmentsAdapter(BaseSegmentsAdapter):
# Add your own logic here
Add the ``PERSONALISATION_SEGMENTS_ADAPTER`` setting to your settings.py and choose your own adapter.

View File

@@ -0,0 +1,99 @@
import time
from django.db.models import F
from personalisation.models import AbstractBaseRule, Segment
class BaseSegmentsAdapter(object):
def setup(self):
return None
def get_all_segments(self):
return None
def get_segment(self):
return None
def add(self):
return None
def refresh(self):
return None
def check_segment_exists(self):
return None
def _test_rules(self, rules, request):
if len(rules) > 0:
for rule in rules:
result = rule.test_user(request)
if result is False:
return False
return True
return False
class Meta:
abstract = True
class SessionSegmentsAdapter(BaseSegmentsAdapter):
def setup(self, request):
self.request = request
self.request.session.setdefault('segments', [])
def get_all_segments(self):
return self.request.session['segments']
def get_segment(self, segment_id):
return next(item for item in self.request.session['segments'] if item.id == segment_id)
def add(self, segment):
def check_if_segmented(item):
"""Check if the user has been segmented"""
return any(seg['encoded_name'] == item.encoded_name for seg in self.request.session['segments'])
if not check_if_segmented(segment):
segdict = {
"encoded_name": segment.encoded_name(),
"id": segment.pk,
"timestamp": int(time.time()),
"persistent": segment.persistent,
}
self.request.session['segments'].append(segdict)
def refresh(self):
current_segments = self.request.session['segments']
persistent_segments = Segment.objects.filter(persistent=True)
current_segments = [item for item in current_segments if
any(seg.pk for seg in persistent_segments) == item['id']]
self.request.session['segments'] = current_segments
segments = Segment.objects.filter(status='enabled').prefetch_related('rules')
for segment in segments:
rules = AbstractBaseRule.__subclasses__()
segment_rules = []
for rule in rules:
segment_rules += segments.rules.filter(segment=segment, pk=rule.pk)
result = self._test_rules(segment_rules, self.request)
if result:
self.add(segment)
for seg in self.request.session['segments']:
segment = Segment.objects.get(pk=seg['id'])
segment.visit_count = F('visit_count') + 1
segment.save()
def check_segment_exists(self, segment):
segments = self.request.session['segments']
return any(item for item in segments if segment.pk == item.id)

View File

@@ -0,0 +1,6 @@
from django.conf import settings
from django.utils.module_loading import import_string
segments_adapter = import_string(getattr(settings, 'PERSONALISATION_SEGMENTS_ADAPTER', 'personalisation.adapters.SessionSegmentsAdapter'))()

View File

@@ -13,6 +13,7 @@ from wagtail.wagtailadmin.widgets import (
from wagtail.wagtailcore import hooks
from personalisation import admin_urls
from personalisation.app_settings import segments_adapter
from personalisation.models import (AbstractBaseRule, PersonalisablePage,
Segment)
from personalisation.utils import impersonate_other_page
@@ -78,81 +79,16 @@ def set_visit_count(page, request, serve_args, serve_kwargs):
@hooks.register('before_serve_page')
def segment_user(page, request, serve_args, serve_kwargs):
if 'segments' not in request.session:
request.session['segments'] = []
segments_adapter.setup(request)
segments_adapter.refresh()
current_segments = request.session['segments']
persistent_segments = Segment.objects.filter(persistent=True)
current_segments = [item for item in current_segments if any(seg.pk for seg in persistent_segments) == item['id']]
request.session['segments'] = current_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, segment.match_any)
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, match_any=False):
"""Test whether the user matches a segment's rules'"""
if len(rules) > 0:
for rule in rules:
result = rule.test_user(request)
if match_any:
if result is True:
return result
elif result is False:
return False
if not match_any:
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()),
"persistent": segment.persistent,
}
request.session['segments'].append(segdict)
@hooks.register('before_serve_page')
def serve_variation(page, request, serve_args, serve_kwargs):
user_segments = []
for segment in request.session['segments']:
for segment in segments_adapter.get_all_segments():
try:
user_segment = Segment.objects.get(pk=segment['id'],
status='enabled')