From 8a078a30db08afb680c48ce94d6e576236febeed Mon Sep 17 00:00:00 2001 From: Radek Czajka Date: Mon, 14 Mar 2022 16:51:23 +0100 Subject: [PATCH 1/1] Better experiments management. --- src/catalogue/models/book.py | 5 +++ src/catalogue/views.py | 8 +++- src/experiments/base.py | 42 +++++++++++++++++++ src/experiments/experiments.py | 19 +++++++++ src/experiments/middleware.py | 32 ++------------ .../templates/experiments/main_switch.html | 27 ++++++------ src/experiments/views.py | 12 ------ src/wolnelektury/settings/custom.py | 12 ------ .../templates/piwik/tracking_code.html | 4 +- 9 files changed, 91 insertions(+), 70 deletions(-) create mode 100644 src/experiments/base.py create mode 100644 src/experiments/experiments.py diff --git a/src/catalogue/models/book.py b/src/catalogue/models/book.py index 9e0f7c8c7..97677bdee 100644 --- a/src/catalogue/models/book.py +++ b/src/catalogue/models/book.py @@ -226,6 +226,11 @@ class Book(models.Model): def isbn_mobi(self): return self.get_extra_info_json().get('isbn_mobi') + def is_accessible_to(self, user): + if not self.preview: + return True + Membership = apps.get_model('club', 'Membership') + return Membership.is_active_for(user) def save(self, force_insert=False, force_update=False, **kwargs): from sortify import sortify diff --git a/src/catalogue/views.py b/src/catalogue/views.py index 62d34bb50..9da3ebbf8 100644 --- a/src/catalogue/views.py +++ b/src/catalogue/views.py @@ -3,6 +3,7 @@ # from collections import OrderedDict import random +import re from django.conf import settings from django.template.loader import render_to_string @@ -286,9 +287,14 @@ def book_detail(request, slug): except Book.DoesNotExist: return pdcounter_views.book_stub_detail(request, slug) + new_layout = request.EXPERIMENTS['layout'] + # Not for preview books. + if new_layout.value and not book.is_accessible_to(request.user): + new_layout.override(None) + return render( request, - 'catalogue/2021/book_detail.html' if request.EXPERIMENTS['layout'] == 'new' else 'catalogue/book_detail.html', + 'catalogue/2021/book_detail.html' if new_layout.value else 'catalogue/book_detail.html', { 'book': book, 'book_children': book.children.all().order_by('parent_number', 'sort_key'), diff --git a/src/experiments/base.py b/src/experiments/base.py new file mode 100644 index 000000000..3430df289 --- /dev/null +++ b/src/experiments/base.py @@ -0,0 +1,42 @@ +import hashlib +from django.conf import settings + + +class Experiment: + slug = None + name = 'experiment' + explicit = False + size = 0 + + def qualify(self, request): + return True + + def __init__(self, request): + self.value = self.get_value(request) + + def override(self, value): + self.value = value + + def get_value(self, request): + overrides = getattr(settings, 'EXPERIMENTS_OVERRIDES', {}) + slug = self.slug + if slug in overrides: + return overrides[slug] + + if self.qualify(request) is False: + return None + + cookie_value = request.COOKIES.get(f'EXPERIMENT_{slug}') + if cookie_value is not None: + if cookie_value == 'on': + return True + elif cookie_value == 'off': + return False + + number = int( + hashlib.md5( + (slug + request.META['REMOTE_ADDR']).encode('utf-8') + ).hexdigest(), + 16 + ) % 10e6 / 10e6 + return number < self.size diff --git a/src/experiments/experiments.py b/src/experiments/experiments.py new file mode 100644 index 000000000..e06d881bb --- /dev/null +++ b/src/experiments/experiments.py @@ -0,0 +1,19 @@ +import re +from .base import Experiment + + +class NewLayout(Experiment): + slug = 'layout' + name = 'Nowy layout strony' + + def qualify(self, request): + if re.search( + 'iphone|mobile|androidtouch', + request.META['HTTP_USER_AGENT'], + re.IGNORECASE): + return False + + +experiments = [ + NewLayout, +] diff --git a/src/experiments/middleware.py b/src/experiments/middleware.py index 080aee2bc..73e93e400 100644 --- a/src/experiments/middleware.py +++ b/src/experiments/middleware.py @@ -1,38 +1,12 @@ -import hashlib -from django.conf import settings +from .experiments import experiments def experiments_middleware(get_response): def middleware(request): exps = {} - overrides = getattr(settings, 'EXPERIMENTS_OVERRIDES', {}) - for exp in settings.EXPERIMENTS: - slug = exp['slug'] - if slug in overrides: - exps[slug] = overrides[slug] - continue - - cookie_value = request.COOKIES.get(f'EXPERIMENT_{slug}') - if cookie_value is not None: - for cohort in exp.get('cohorts', []): - if cohort['value'] == cookie_value: - exps[slug] = cookie_value - break - - if slug not in exps: - number = int( - # TODO sth else? - hashlib.md5( - (slug + request.META['REMOTE_ADDR']).encode('utf-8') - ).hexdigest(), - 16 - ) % 10e6 / 10e6 - for cohort in exp.get('cohorts', []): - number -= cohort.get('size', 1) - if number < 0: - exps[slug] = cohort['value'] - break + for exp in experiments: + exps[exp.slug] = exp(request) request.EXPERIMENTS = exps response = get_response(request) diff --git a/src/experiments/templates/experiments/main_switch.html b/src/experiments/templates/experiments/main_switch.html index 0ff0d88c4..fbdf863e6 100644 --- a/src/experiments/templates/experiments/main_switch.html +++ b/src/experiments/templates/experiments/main_switch.html @@ -4,21 +4,20 @@ {% block body %} - {% for exp in experiments %} -
- {{ exp.config.name }} - {% for cohort in exp.config.cohorts %} - - {% endfor %} + {% for exp in request.EXPERIMENTS.values %} +
+ {{ exp.name }} + +
- - - {% endfor %} {% endblock %} diff --git a/src/experiments/views.py b/src/experiments/views.py index 71c514acd..001b32a7f 100644 --- a/src/experiments/views.py +++ b/src/experiments/views.py @@ -1,17 +1,5 @@ from django.views.generic import TemplateView -from django.conf import settings class MainSwitchView(TemplateView): template_name = 'experiments/main_switch.html' - - def get_context_data(self): - ctx = super().get_context_data() - ctx['experiments'] = [ - { - "config": conf, - "value": self.request.EXPERIMENTS.get(conf['slug']) - } - for conf in settings.EXPERIMENTS - ] - return ctx diff --git a/src/wolnelektury/settings/custom.py b/src/wolnelektury/settings/custom.py index 08937d9f8..68a2153a8 100644 --- a/src/wolnelektury/settings/custom.py +++ b/src/wolnelektury/settings/custom.py @@ -39,18 +39,6 @@ CLUB_PAYU_POS = '300746' CLUB_PAYU_RECURRING_POS = '300746' CLUB_APP_HOST = None - -EXPERIMENTS = [ - { - "name": "Eksperymentalny układ strony utworu", - "slug": "layout", - "cohorts": [ - {"size": 0, "value": "new", "name": "eksperymentalny układ", "explicit": True}, - { "value": "old", "name": "stary układ"}, - ], - }, -] - MESSAGING_MIN_DAYS = 2 NEWSLETTER_PHPLIST_SUBSCRIBE_URL = None diff --git a/src/wolnelektury/templates/piwik/tracking_code.html b/src/wolnelektury/templates/piwik/tracking_code.html index 53be0f3cb..b70225eea 100644 --- a/src/wolnelektury/templates/piwik/tracking_code.html +++ b/src/wolnelektury/templates/piwik/tracking_code.html @@ -1,8 +1,8 @@