Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
4111947
Support wagtail 5 and django 4.2
TopDevPros Jul 22, 2023
7135b36
Revert heading to 'Installation'
gasman Aug 9, 2023
f3a3ca7
Remove / fix docstrings
gasman Aug 9, 2023
16e63c5
Remove unnecessary fallbacks
gasman Aug 9, 2023
488fbdc
revert comment as per https://github.com/torchbox/wagtail-experiments…
gasman Aug 9, 2023
2072633
Downcase verbose_names
gasman Aug 9, 2023
eb842b3
set version number to 0.3 in setup.py
gasman Aug 9, 2023
436418c
revert change to fixture
gasman Aug 9, 2023
43e4c0a
Remove unwanted copy of simple_page template
gasman Aug 9, 2023
2550c21
Remove unnecessary fallback caused by incorrectly capitalised verbose…
gasman Aug 9, 2023
78712d7
Revert changes to impersonate_other_page that stop it from replacing …
gasman Aug 9, 2023
0abffa9
Add Github Actions config
gasman Aug 10, 2023
8b4aef9
run tests via runtests.py
gasman Aug 10, 2023
c86ce81
Remove travis CI config
gasman Aug 10, 2023
87d5f67
set 3.8 as minimum python version
gasman Aug 10, 2023
34dcea3
Add nightly build test scripts
gasman Aug 10, 2023
ff40d73
deliberate test failure to test nightly build reporting
gasman Aug 10, 2023
a2ddfb3
Revert "deliberate test failure to test nightly build reporting"
gasman Aug 10, 2023
4a2ea36
Add build to gitignore
gasman Aug 10, 2023
6a20507
Fill in release date for 0.3
gasman Aug 10, 2023
ad77595
Use external modeladmin package where available
gasman Nov 6, 2023
0b688a2
Test against Wagtail 5.2 and Python 3.12
gasman Nov 6, 2023
2dc1427
Remove tox.ini as it is clearly unused and unmaintained
gasman Nov 6, 2023
52da076
Update Github Actions to support installing wagtail-modeladmin package
gasman Nov 6, 2023
e5700ca
Version bump to 0.3.1
gasman Nov 6, 2023
1d3a134
Allow for standalone URL to be use as goals
dawnwages Apr 21, 2020
ad11473
goal_url issue
dawnwages Apr 21, 2020
ed0c8a6
II-210: URLS included in experiment
dawnwages Apr 22, 2020
3589c9f
#210: fix migrations to match other wagtail-experiments
dawnwages Apr 22, 2020
520f97d
#210: path and url
dawnwages Apr 23, 2020
e692941
#568: record_participant and record_completion print to console
dawnwages Apr 24, 2020
37a2b83
Testing pdb statements
dawnwages Apr 24, 2020
c7b74b8
#210: clean up debugging
dawnwages Apr 29, 2020
3190ef8
#210: Path and query
dawnwages Apr 23, 2020
8785c5f
#210: Path and query
dawnwages Apr 23, 2020
812dab9
fix path
dawnwages Apr 29, 2020
0622a5c
fix
dawnwages Apr 29, 2020
01ad095
Merge branch 'master' into version-0.3.1-update
DelGall May 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/report_nightly_build_failure.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

"""
Called by GitHub Action when the nightly build fails.
This reports an error to the #nightly-build-failures Slack channel.
"""
import os
import requests

if "SLACK_WEBHOOK_URL" in os.environ:
# https://docs.github.com/en/free-pro-team@latest/actions/reference/environment-variables#default-environment-variables
repository = os.environ["GITHUB_REPOSITORY"]
run_id = os.environ["GITHUB_RUN_ID"]
url = f"https://github.com/{repository}/actions/runs/{run_id}"

print("Reporting to #nightly-build-failures slack channel")
response = requests.post(
os.environ["SLACK_WEBHOOK_URL"],
json={
"text": f"A Nightly build failed. See {url}",
},
)

print(f"Slack responded with: {response}")

else:
print(
"Unable to report to #nightly-build-failures slack channel because SLACK_WEBHOOK_URL is not set"
)
35 changes: 35 additions & 0 deletions .github/workflows/nightly-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Nightly Wagtail test

on:
schedule:
- cron: "20 1 * * *"

workflow_dispatch:

jobs:
nightly-test:
# Cannot check the existence of secrets, so limiting to repository name to prevent all forks to run nightly.
# See: https://github.com/actions/runner/issues/520
if: ${{ github.repository == 'torchbox/wagtail-experiments' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.9
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install "git+https://github.com/wagtail/wagtail.git@main#egg=wagtail"
pip install -e .[testing]
- name: Test
id: test
continue-on-error: true
run: ./runtests.py
- name: Send Slack notification on failure
if: steps.test.outcome == 'failure'
run: |
python .github/report_nightly_build_failure.py
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
85 changes: 85 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@

name: CI

on:
push:
branches: [ main ]
pull_request:

# Current configuration:
# - django 3.2, python 3.8, wagtail 4.1, postgres
# - django 4.0, python 3.9, wagtail 5.0, sqlite
# - django 4.1, python 3.10, wagtail 5.1, postgres
# - django 4.2, python 3.11, wagtail 5.2, sqlite
# - django 4.2, python 3.12, wagtail main, sqlite (allow failures)
jobs:
test:
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.experimental }}
strategy:
matrix:
include:
- python: "3.8"
django: "Django>=3.2,<4.0"
wagtail: "wagtail>=4.1,<4.2"
database: "postgresql"
modeladmin: false
experimental: false
- python: "3.9"
django: "Django>=4.0,<4.1"
wagtail: "wagtail>=5.0,<5.1"
database: "sqlite3"
modeladmin: false
experimental: false
- python: "3.10"
django: "Django>=4.1,<4.2"
wagtail: "wagtail>=5.1,<5.2"
database: "postgresql"
modeladmin: true
experimental: false
- python: "3.11"
django: "Django>=4.2,<4.3"
wagtail: "wagtail>=5.2,<5.3"
database: "sqlite3"
modeladmin: true
experimental: false

- python: "3.12"
django: "Django>=4.2,<4.3"
wagtail: "git+https://github.com/wagtail/wagtail.git@main#egg=wagtail"
database: "sqlite3"
modeladmin: true
experimental: true

services:
postgres:
image: postgres:14
env:
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5

steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install "psycopg2>=2.9.3"
pip install "${{ matrix.django }}"
pip install "${{ matrix.wagtail }}"
pip install -e .[testing]
- name: Install wagtail-modeladmin
if: ${{ matrix.modeladmin }}
run: pip install wagtail-modeladmin
- name: Test
run: ./runtests.py
env:
DATABASE_ENGINE: django.db.backends.${{ matrix.database }}
DATABASE_HOST: localhost
DATABASE_USER: postgres
DATABASE_PASS: postgres
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
*.pyc
/.tox/
/dist/
/build/
/wagtail_experiments.egg-info/
39 changes: 0 additions & 39 deletions .travis.yml

This file was deleted.

15 changes: 15 additions & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
Changelog
=========

0.3.1 (2023-11-06)
~~~~~~~~~~~~~~~~~~

* Use external wagtail-modeladmin package where available
* Added support for Wagtail 5.1 - 5.2 and provisional support for Wagtail 6.0


0.3 (2023-08-10)
~~~~~~~~~~~~~~~~

* Added support for Wagtail 4.1 thru 5.0
* Added support for Django 3.2 thru 4.2
* Added docstrings to all 'experiment' functions and classes.
* Support internationalization for models

0.2 (28.11.2018)
~~~~~~~~~~~~~~~~

Expand Down
32 changes: 27 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
Wagtail Experiments
===================

.. image:: https://api.travis-ci.org/torchbox/wagtail-experiments.svg?branch=master
:target: https://travis-ci.org/torchbox/wagtail-experiments

**A/B testing for Wagtail**

This module supports the creation of A/B testing experiments within a Wagtail site. Several alternative versions of a page are set up, and on visiting a designated control page, a user is presented with one of those variations, selected at random (using a simplified version of the `PlanOut algorithm <https://facebook.github.io/planout/>`_). The number of visitors receiving each variation is logged, along with the number that subsequently go on to complete the experiment by visiting a designated goal page.
Expand All @@ -14,7 +11,32 @@ This module supports the creation of A/B testing experiments within a Wagtail si
Installation
------------

wagtail-experiments is compatible with Wagtail 1.7 to 2.3, and Django 1.8 to 2.1. To install::
wagtail-experiments is compatible with Wagtail 4.1 to 5.2, and Django 3.2 to 4.2. It depends on the Wagtail ModelAdmin module, which is available as an external package as of Wagtail 5.0; we recommend using this rather than the bundled `wagtail.contrib.modeladmin` module to avoid deprecation warnings. The external package will be required as of Wagtail 6.0.

### On Wagtail 5.0 and above

To install::

pip install wagtail-experiments wagtail-modeladmin

and ensure that the apps ``wagtail_modeladmin`` and ``experiments`` are included in your project's ``INSTALLED_APPS``:

.. code-block:: python

INSTALLED_APPS = [
# ...
'wagtail_modeladmin',
'experiments',
# ...
]

Then migrate::

./manage.py migrate

### On Wagtail 4.x

To install::

pip install wagtail-experiments

Expand All @@ -29,7 +51,7 @@ and ensure that the apps ``wagtail.contrib.modeladmin`` and ``experiments`` are
# ...
]

Then migrate::
Then migrate::

./manage.py migrate

Expand Down
10 changes: 4 additions & 6 deletions experiments/admin_urls.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
from __future__ import absolute_import, unicode_literals

from django.urls import path
from django.urls import re_path
from experiments import views

app_name = 'experiments'

urlpatterns = [
path('experiment/report/<int:experiment_id>/', views.experiment_report, name='report'),
path('experiment/select_winner/<int:experiment_id>/<int:variation_id>/', views.select_winner, name='select_winner'),
path('experiment/report/preview/<int:experiment_id>/<int:page_id>/', views.preview_for_report, name='preview_for_report'),
re_path(r'^experiment/report/(\d+)/$', views.experiment_report, name='report'),
re_path(r'^experiment/select_winner/(\d+)/(\d+)/$', views.select_winner, name='select_winner'),
re_path(r'^experiment/report/preview/(\d+)/(\d+)/$', views.preview_for_report, name='preview_for_report'),
]
42 changes: 38 additions & 4 deletions experiments/backends/db.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
from __future__ import absolute_import, unicode_literals

import datetime

from django.db.models import F, Sum

from experiments.models import ExperimentHistory

#TODO: potentially the same session? clearing cookies
def record_participant(experiment, user_id, variation, request):
'''
If the user hasn't already participated in this experiment,
then save the variation the user viewed.

Args:
experiment: instance of experiments.models.Experiment
user_id: the id for this user.
variation: variation user viewed.
request: django HttpRequest.

Return:
Nothing
'''

# abort if this user has participated already
experiments_started = request.session.get('experiments_started', [])
if experiment.id in experiments_started:
Expand All @@ -23,8 +34,21 @@ def record_participant(experiment, user_id, variation, request):
# increment the participant_count
ExperimentHistory.objects.filter(pk=history.pk).update(participant_count=F('participant_count') + 1)


def record_completion(experiment, user_id, variation, request):
'''
If the user has started this experiment, but not completed it yet,
then mark the user's participation as completed.

Args:
experiment: instance of experiments.models.Experiment
user_id: the id for this user.
variation: variation user viewed.
request: django HttpRequest.

Return:
Nothing
'''

# abort if this user never started the experiment
if experiment.id not in request.session.get('experiments_started', []):
return
Expand All @@ -46,6 +70,16 @@ def record_completion(experiment, user_id, variation, request):


def get_report(experiment):
'''
Generate a report about the experiment's results.

Args:
experiment: instance of experiments.models.Experiment

Return:
A report of experiment results as a dictionary.
'''

result = {}
result.setdefault('variations', [])

Expand Down
Loading
Loading