Есть какие-нибудь мысли о A / B-тестировании в проекте на основе Django? - PullRequest
57 голосов
/ 15 апреля 2009

Мы только сейчас начали проводить A / B-тестирование для нашего проекта на основе Django. Могу ли я получить некоторую информацию о передовых практиках или полезную информацию об этом A / B-тестировании.

В идеале каждая новая тестовая страница будет выделяться одним параметром (как в Gmail). mysite.com/?ui=2 должен дать другую страницу. Поэтому для каждого представления мне нужно написать декоратор для загрузки различных шаблонов, основанных на значении параметра 'ui'. И я не хочу жестко кодировать любые имена шаблонов в декораторах. Итак, каким будет шаблон urls.py url?

Ответы [ 8 ]

95 голосов
/ 15 мая 2009

Полезно сделать шаг назад и абстрагироваться от того, что пытается сделать A / B-тестирование, прежде чем углубляться в код. Что именно нам понадобится для проведения теста?

  • Цель с условием
  • По крайней мере, два разных Пути, чтобы выполнить условие цели
  • Система для отправки зрителей по одному из путей
  • Система регистрации результатов теста

Имея это в виду, давайте подумаем о реализации.

Цель

Когда мы думаем о цели в Интернете, обычно мы подразумеваем, что пользователь достигает определенной страницы или что он выполняет определенное действие, например, успешно зарегистрировавшись в качестве пользователя или перейдя на страницу оформления заказа.

В Django мы могли бы смоделировать это несколькими способами - возможно, наивно внутри представления, вызывая функцию всякий раз, когда цель была достигнута:

    def checkout(request):
        a_b_goal_complete(request)
        ...

Но это не помогает, потому что мы должны добавлять этот код везде, где нам это нужно - плюс, если мы используем какие-либо подключаемые приложения, мы бы предпочли не редактировать их код, чтобы добавить наш A / B-тест.

Как мы можем ввести Цели A / B без непосредственного редактирования кода вида? Как насчет Middleware?

    class ABMiddleware:
      def process_request(self, request):
          if a_b_goal_conditions_met(request):
            a_b_goal_complete(request)

Это позволило бы нам отслеживать цели А / Б в любом месте сайта.

Откуда мы знаем, что условия Цели были выполнены? Для простоты реализации я предполагаю, что мы знаем, что цель достигла своих условий, когда пользователь достигает определенного URL-пути. В качестве бонуса мы можем измерить это, не пачкая руки внутри вида. Возвращаясь к нашему примеру регистрации пользователя, можно сказать, что эта цель была достигнута, когда пользователь достигает пути URL:

/ регистрация / полная

Итак, мы определяем a_b_goal_conditions_met:

     a_b_goal_conditions_met(request):
       return request.path == "/registration/complete":

Дорожка

Размышляя о путях в Django, естественно перейти к идее использования разных шаблонов. Есть ли другой путь, еще предстоит выяснить. В A / B-тестировании вы делаете небольшие различия между двумя страницами и измеряете результаты. Поэтому рекомендуется определить единый базовый шаблон пути, из которого должны быть расширены все пути к цели.

Как визуализировать эти шаблоны? Декоратор, вероятно, является хорошим началом - в Django рекомендуется включать параметр template_name в ваши представления, чтобы декоратор мог изменить этот параметр во время выполнения.

    @a_b
    def registration(request, extra_context=None, template_name="reg/reg.html"):
       ...

Вы можете видеть, что этот декоратор либо анализирует упакованную функцию и модифицирует аргумент template_name, либо просматривает правильные шаблоны откуда-то (например, модель). Если мы не хотим добавлять декоратор к каждой функции, мы можем реализовать это как часть нашего ABMiddleware:

    class ABMiddleware:
       ...
       def process_view(self, request, view_func, view_args, view_kwargs):
         if should_do_a_b_test(...) and "template_name" in view_kwargs:
           # Modify the template name to one of our Path templates
           view_kwargs["template_name"] = get_a_b_path_for_view(view_func)
           response = view_func(view_args, view_kwargs)
           return response

Нам также нужно было бы добавить какой-то способ, чтобы отслеживать, какие виды имеют A / B-тесты и т. Д.

Система для отправки зрителей по Пути

Теоретически это легко, но существует множество различных реализаций, поэтому неясно, какая из них лучше. Мы знаем, что хорошая система должна равномерно делить пользователей по пути - Нужно использовать какой-то метод хэширования - Возможно, вы могли бы использовать модуль счетчика memcache, деленный на количество путей - возможно, есть лучший способ.

Система для записи результатов теста

Нам нужно записать, сколько пользователей прошли по какому пути - нам также понадобится доступ к этой информации, когда пользователь достигнет цели (нам нужно иметь возможность сказать, по какому пути они прошли, чтобы выполнить условие цели ) - мы будем использовать какую-то модель (-и) для записи данных, а также сеансы Django или файлы cookie для сохранения информации о пути до тех пор, пока пользователь не выполнит условие цели.

Заключительные мысли

Я дал много псевдокода для реализации A / B-тестирования в Django - вышеизложенное ни в коем случае не является полным решением, но хорошим началом для создания многократно используемой платформы для A / B-тестирования в Django.

Для справки, вы можете посмотреть на Seminute A / B Пола Мара на GitHub - это версия ROR выше! http://github.com/paulmars/seven_minute_abs/tree/master


Обновление

Что касается дальнейшего изучения и изучения Оптимизатора веб-сайтов, очевидно, что в приведенной выше логике есть зияющие дыры. Используя различные шаблоны для представления путей, вы нарушаете все кэширование в представлении (или, если представление кэшируется, оно всегда будет использовать один и тот же путь!). Вместо использования путей, я бы вместо этого украл терминологию GWO и использовал идею Combinations - это одна конкретная часть изменения шаблона - например, изменение тега <h1> сайта.

Решение будет включать теги шаблона, которые будут отображаться в JavaScript. Когда страница загружается в браузер, JavaScript отправляет запрос на ваш сервер, который выбирает одну из возможных комбинаций.

Таким образом, вы можете протестировать несколько комбинаций на странице, сохраняя при этом кеширование!


Update

Все еще есть место для переключения шаблонов - скажем, например, вы вводите совершенно новую домашнюю страницу и хотите проверить ее производительность по сравнению со старой домашней страницей - вы все равно хотите использовать технику переключения шаблонов. Имейте в виду, что вам нужно будет найти способ переключаться между X количеством кэшированных версий страницы. Для этого вам нужно переопределить стандартное кэшированное промежуточное программное обеспечение, чтобы увидеть, является ли оно A / B-тестом, выполняющимся на запрошенном URL-адресе. Тогда он мог бы выбрать правильную кэшированную версию, чтобы показать !!!


Обновление

Используя идеи, описанные выше, я реализовал подключаемое приложение для базового A / B-тестирования Django. Вы можете получить его с Github:

http://github.com/johnboxall/django-ab/tree/master

12 голосов
/ 29 октября 2009

Django Lean - хороший вариант для A / B-тестирования

http://bitbucket.org/akoha/django-lean/wiki/Home

7 голосов
/ 16 апреля 2009

Если вы используете параметры GET, такие как suggsted (?ui=2), то вам вообще не нужно трогать urls.py. Ваш декоратор может проверить request.GET['ui'] и найти то, что ему нужно.

Чтобы избежать жесткого кодирования имен шаблонов, может быть, вы могли бы обернуть возвращаемое значение из функции представления? Вместо того, чтобы возвращать выходные данные render_to_response, вы можете вернуть кортеж (template_name, context) и позволить декоратору манипулировать именем шаблона. Как насчет этого? ВНИМАНИЕ: я не проверял этот код

def ab_test(view):
    def wrapped_view(request, *args, **kwargs):
        template_name, context = view(request, *args, **kwargs)
        if 'ui' in request.GET:
             template_name = '%s_%s' % (template_name, request.GET['ui'])
             # ie, 'folder/template.html' becomes 'folder/template.html_2'
        return render_to_response(template_name, context)
    return wrapped_view

Это действительно простой пример, но я надеюсь, что он поможет понять идею. Вы можете изменить несколько других вещей об ответе, таких как добавление информации в контекст шаблона. Вы можете использовать эти контекстные переменные для интеграции с аналитикой вашего сайта, например, с Google Analytics.

В качестве бонуса вы можете провести рефакторинг этого декоратора в будущем, если вы решите прекратить использование параметров GET и перейти к чему-либо на основе файлов cookie и т. Д.

Обновление Если у вас уже написано много представлений, и вы не хотите изменять их все, вы можете написать собственную версию render_to_response.

def render_to_response(template_list, dictionary, context_instance, mimetype):
    return (template_list, dictionary, context_instance, mimetype)

def ab_test(view):
    from django.shortcuts import render_to_response as old_render_to_response
    def wrapped_view(request, *args, **kwargs):
        template_name, context, context_instance, mimetype = view(request, *args, **kwargs)
        if 'ui' in request.GET:
             template_name = '%s_%s' % (template_name, request.GET['ui'])
             # ie, 'folder/template.html' becomes 'folder/template.html_2'
        return old_render_to_response(template_name, context, context_instance=context_instance, mimetype=mimetype)
    return wrapped_view

@ab_test
def my_legacy_view(request, param):
     return render_to_response('mytemplate.html', {'param': param})
1 голос
/ 19 декабря 2014

Джанго-худ выглядит потрясающе. Я собираюсь попытаться понять это все еще. Я закончил тем, что катил свое собственное решение, которого достаточно для того, что я пытался сделать. Я попытался упаковать его и сделать его удобным для начинающих. Это супер базовый, попробуйте:

https://github.com/crobertsbmw/RobertsAB

1 голос
/ 28 мая 2013

Эти ответы кажутся несвежими. В настоящее время Google Analytics является, пожалуй, самым популярным и лучшим бесплатным вариантом для большинства сайтов. Вот некоторые ресурсы для интеграции django с Google Analytics:

Плагины

Как Тос :

1 голос
/ 20 мая 2010

Код на основе кода Джастина Восса:

def ab_test(force = None):
    def _ab_test(view):
        def wrapped_view(request, *args, **kwargs):
            request, template_name, cont = view(request, *args, **kwargs)
            if 'ui' in request.GET:
                request.session['ui'] = request.GET['ui']
            if 'ui' in request.session:
                cont['ui'] = request.session['ui']
            else:
                if force is None:
                    cont['ui'] = '0'
                else:
                    return redirect_to(request, force)
            return direct_to_template(request, template_name, extra_context = cont)
        return wrapped_view
    return _ab_test

пример функции с использованием кода:

@ab_test()
def index1(request):
    return (request,'website/index.html', locals())

@ab_test('?ui=33')
def index2(request):
    return (request,'website/index.html', locals())

Что здесь происходит: 1. Переданный параметр пользовательского интерфейса сохраняется в переменной сеанса 2. Каждый раз загружается один и тот же шаблон, но контекстная переменная {{ui}} хранит идентификатор пользовательского интерфейса (вы можете использовать его для изменения шаблона) 3. Если пользователь заходит на страницу без? Ui = xx, то в случае index2 он перенаправляется на '? Ui = 33', в случае index1 переменная пользовательского интерфейса устанавливается на 0.

Я использую 3 для перенаправления с главной страницы в Оптимизатор веб-сайта, который, в свою очередь, перенаправляет обратно на главную страницу с правильным параметром? Ui.

1 голос
/ 16 апреля 2009

Ответ Джастина правильный ... Я рекомендую вам проголосовать за него, так как он был первым. Его подход особенно полезен, если у вас есть несколько видов, которые нуждаются в этой настройке A / B.

Заметьте, однако, что вам не нужен декоратор или изменения в urls.py, если у вас всего несколько представлений. Если вы оставили свой файл urls.py как есть ...

(r'^foo/', my.view.here),

... вы можете использовать request.GET для определения запрошенного варианта просмотра:

def here(request):
    variant = request.GET.get('ui', some_default)

Если вы хотите избежать жесткого кодирования имен шаблонов для отдельных представлений A / B / C / etc, просто сделайте их условными обозначениями в вашей схеме именования шаблонов (как рекомендует подход Джастина):

def here(request):
    variant = request.GET.get('ui', some_default)
    template_name = 'heretemplates/page%s.html' % variant
    try:
        return render_to_response(template_name)
    except TemplateDoesNotExist:
        return render_to_response('oops.html')
0 голосов
/ 13 ноября 2013

Я написал пример сплит-тестирования для django, который может пригодиться любому, кто изучит это -

https://github.com/DanAncona/django-mini-lean

Мне нравится слышать, что вы думаете об этом, и как я могу сделать это более полезным!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...