django-AB-project

Simple and easy-to-use project for A/B testing.


License
MIT
Install
pip install django-AB-project==1.1.0

Documentation

Django-AB-project

Here is my first package for Django. I tried to write it so that it would be easy for you to use this package.

It provide four function in utils.py and some models in models.py.

This would be easy to used this package with Generic views in Django, but this can be work properly in methods, just used another technic.

(You can read about Generic Views in Django here - https://docs.djangoproject.com/en/1.11/topics/class-based-views/)

What is A/B testing?

A/B testing (also known as split testing) is a method of comparing two versions of a webpage against each other to determine which is better for users. AB testing is essentially an experiment where two variations of a page are shown to users in random order (In this package all users are divided to two group, and not needed to registrated in your app) and statistical analysis is used to determine which variation performs better for a given conversion goal.

How to install?

1. Use pip to download this package - pip install django-AB-project
2. Add 'ab', to INSTALLED_APPS in settings.py:
  INSTALLED_APPS = [
    ...
    ...
    'ab',
  ]
  1. Configure your sessions - set in settings.py at the end of file (or where you want):
  SESSION_EXPIRE_AT_BROWSER_CLOSE = True # Coockie's will be destroyed after browser close
  SESSION_COOKIE_AGE = 60 * 60 * 24 # Coockie's will be destroyed after 24 hours

You can edit this lines if you need.

4. Run python manage.py makemigrations, and python manage.py migrate

5. Run server (python manage.py runserver) and go to the admin page. If you see new line named "Ab_Split_Testing" and can click on them without error page - Congratulation! You successfully installed this package.

How to use?

This package have two main function for each version of views
(Generic views version)

Let's imagine we have some ListView in views.py

  from django.views.generic import ListView
  from split_testing.utils import ab_init, success_goal

  class IntroPage(ListView):
    model = YourModel
    template_name = 'pages/intro.html'

First function, called ab_init we need to use when page is loading, to check if user is load this page.

But also, we need some varables (like path to alternative html file, and name for test)

Let's define!

  class IntroPage(ListView):
    model = YourModel
    template_name = 'pages/intro.html'
    alternative_template_name = 'alt_pages/intro.html'
    page_name = 'intro page'

    def dispatch(self, request, *args, **kwargs):
        # init for a/b, add +1 to "Entered"
        ab_init(self)
        # call the view
        return super(IntroPage, self).dispatch(request, *args, **kwargs)

Let see what we got:

   alternative_template_name = 'alt_pages/intro.html'

Here you need to enter a path to your alternative html file, which you want to test with template_file

Next is:

   page_name = 'intro page'

Enter a name for this page(this test will be named like that)

Next scary thing:

    def dispatch(self, request, *args, **kwargs):
        # init for a/b, add +1 to "Entered"
        ab_init(self)
        # call the view
        return super(IntroPage, self).dispatch(request, *args, **kwargs)

This function will be called when page is loading, so it's perfect place where a/b will be initialized

Let's test our page, go to the urls.py, and write this code:

    from django.conf.urls import url
    from . import views

    urlpatterns = [
         url(r'^$', views.IntroPage.as_view(), name='intro_page'),
    ]

Run your server, and go to 127.0.0.1:8000/

If you don't see any error, and page looks like normal - go to the admin page and check "Ab_Split_Testing"!

Here you see new created object, using your page_name. Click on and you will see some data. Run another browser, or re-enter in your browser and load main page again, and you will see changes.

We successfully initialized our test, but we need collect not only users who entered to our page, but also users, who will make success actions. It would be simple if you need to test forms on your page, but we can test other element's too, just use a little bit of JavaScript.

Testing forms (FormView)

You just need to edit your form_valid method:

from django.shortcuts import redirect
from django.views.generic import FormView
from split_testing.utils import ab_init, success_goal

class SecondPage(FormView):
    # A/B testing variables
    template_name = 'pages/second_page.html'
    alternative_template_name = 'alt_pages/second_page.html'
    page_name = 'second page'
    form_class = someForm
    
    def dispatch(self, request, *args, **kwargs):
        # init for a/b, add +1 to "Entered"
        ab_init(self)
        # call the view
        return super(SecondPage, self).dispatch(request, *args, **kwargs)
        
    def form_valid(self, form):
        # Process your forms
        ... 
        ...
        # A/B set successfully goal before redirect
        success_goal(self)
        
        return redirect('third_page')

success_goal(self) is second function what this package implement.

Testing using JS (Any other View)

First, you need edit your both html files. Just add to bottom of the body tag this function:

  <script type="text/javascript">
    function Sendgoal(url_page) {
      $.ajax({
        url: url_page,
        data: {'is_clicked': 'True',
                csrfmiddlewaretoken: "{{ csrf_token }}"},
        type:"POST",
        success: function (data) {
          console.log('POST success');
        },
      });
    }
  </script>

Then, you need find your button, or link which pressed by user will be send goal to server

<a href='#' onclick="Sendgoal('{% url 'intro_page' %}')">Click me!</a>

We need to edit our IntroPage class, to handle POST request:

class IntroPage(ListView):
    model = YourModel
    template_name = 'pages/intro.html'
    alternative_template_name = 'alt_pages/intro.html'
    page_name = 'intro page'

    def dispatch(self, request, *args, **kwargs):
        # init for a/b, add +1 to "Entered"
        ab_init(self)
        # call the view
        return super(IntroPage, self).dispatch(request, *args, **kwargs)
        
    def post(self, request, *args, **kwargs):
        is_clicked = request.POST.get('is_clicked')
        if is_clicked == 'True':
            # A/B set success goal
            success_goal(self)
            return JsonResponse({'OK':'OK'})
        # if POST request is not equal what we send in template
        return JsonResponse({'KO':'KO'})

That's it! Your test is ready, try to test it using another browser.


(Method views version)

Coming soon!