Какие-либо мысли о A/B, тестирующем в основанном на Django проекте?

Похоже, вас интересуют только значения вашего вложенного хэша. В этом случае вы можете просто сделать

titles = hash.values.map(&:keys).flatten.uniq
rows = hash.values.map { |data| data.values_at(*titles) }

s = CSV.generate do |csv|
  csv << titles
  rows.each do |row|
    csv << row
  end
end

(обновить)

Заголовки должны быть сведены в простой массив.

56
задан None-da 15 April 2009 в 18:15
поделиться

5 ответов

Ответ Джастина правильный ... Я рекомендую вам проголосовать за него, так как он был первым. Его подход особенно полезен, если у вас есть несколько представлений, которые нуждаются в этой настройке 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')
1
ответ дан Jarret Hardie 26 November 2019 в 17:17
поделиться

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

Чтобы избежать жесткого кодирования имен шаблонов, возможно, вы могли бы обернуть возвращаемое значение из функции view? Вместо того, чтобы возвращать выходные данные 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})
7
ответ дан 26 November 2019 в 17:17
поделиться

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

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

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

Цель

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

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

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

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

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

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

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

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

/ registration / complete

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

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

Paths

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

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

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

Вы могли видеть, что этот декоратор либо исследует обернутую функцию и изменение аргумента имя_шаблона или поиск правильных шаблонов откуда-то (например, модели). Если бы мы не сделали Я хочу добавить декоратор к каждой функции, которую мы могли бы реализовать как часть нашего 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-тесты и т. д.

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

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

Система для записи Результаты теста

Нам нужно записать, сколько пользователей пошли по тому пути - мы » http://github.com/paulmars/seven_minute_abs/tree/master


Обновление

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

сайта.

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

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


Обновление

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


Обновление

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

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

94
ответ дан 26 November 2019 в 17:17
поделиться

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

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

12
ответ дан 26 November 2019 в 17:17
поделиться

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

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. Переданный параметр UI сохраняется в переменной сессии 2. Каждый раз загружается один и тот же шаблон, но контекстная переменная {{ui}} хранит идентификатор UI (вы можете использовать его для изменения шаблона). 3. Если пользователь заходит на страницу без ?ui=xx, то в случае index2 он перенаправляется на '?ui=33', в случае index1 переменная UI устанавливается в 0.

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

1
ответ дан 26 November 2019 в 17:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: