Files
odoo_source/addons/website_version/models/experiment.py
T

177 lines
8.8 KiB
Python

# -*- coding: utf-8 -*-
from openerp.exceptions import Warning
from openerp import models, fields, api
from openerp.exceptions import ValidationError
from openerp.tools.translate import _
TOO_MUCH_EXPERIMENTS = 2
OVERLAP_EXPERIMENT = 1
CREATE_EXPERIMENT = 0
class Experiment_version(models.Model):
""" Allow to define the versions contained in an experiment.
The frequency is a ponderation to determine the probability to visit a version in an experiment.
The googe_index is the index of a version in an experiment, used to send data to Google Analytics.
"""
_name = "website_version.experiment.version"
_rec_name = "version_id"
version_id = fields.Many2one('website_version.version', string="Version", required=True, ondelete='cascade')
experiment_id = fields.Many2one('website_version.experiment', string="Experiment", required=True, ondelete='cascade')
frequency = fields.Selection([('10', 'Less'), ('50', 'Medium'), ('80', 'More')], string='Frequency', default='50')
google_index = fields.Integer(string='Google index')
class Goals(models.Model):
""" Allow to define the goal of an experiment.
The goals are defined in the Google Analytics account and can be synchronised in backend.
"""
_name = "website_version.goals"
name = fields.Char(string="Name", required=True)
google_ref = fields.Char(string="Google Reference", required=True)
class Experiment(models.Model):
"""An experiment pointed to some experiment_versions and dispatch each website visitor to a version.
"""
_name = "website_version.experiment"
_inherit = ['mail.thread']
_order = 'sequence'
@api.multi
@api.constrains('state')
def _check_view(self):
#No overlap for running experiments
for exp in self:
if exp.state == 'running':
for exp_ver in exp.experiment_version_ids:
if exp_ver.search([('version_id.view_ids.key', 'in', [v.key for v in exp_ver.version_id.view_ids]), ('experiment_id', '!=', exp_ver.experiment_id.id), ('experiment_id.website_id', '=', exp_ver.experiment_id.website_id.id), ('experiment_id.state', '=', 'running')]):
raise ValidationError('This experiment contains a view which is already used in another running experience')
return True
@api.multi
@api.constrains('website_id', 'experiment_version_ids')
def _check_website(self):
for exp in self:
for exp_ver in exp.experiment_version_ids:
if not exp_ver.version_id.website_id.id == exp.website_id.id:
raise ValidationError('This experiment must have versions which are in the same website')
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False, lazy=True):
""" Override read_group to always display all states. """
if groupby and groupby[0] == "state":
# Default result structure
# states = self._get_state_list(cr, uid, context=context)
states = [('running', 'Running'), ('paused', 'Paused'), ('ended', 'Ended')]
read_group_all_states = [{
'__context': {'group_by': groupby[1:]},
'__domain': domain + [('state', '=', state_value)],
'state': state_value,
'state_count': 0,
} for state_value, state_name in states]
# Get standard results
read_group_res = super(Experiment, self).read_group(cr, uid, domain, fields, groupby, offset=offset, limit=limit, context=context, orderby=orderby)
# Update standard results with default results
result = []
for state_value, state_name in states:
res = filter(lambda x: x['state'] == state_value, read_group_res)
if not res:
res = filter(lambda x: x['state'] == state_value, read_group_all_states)
res[0]['state'] = [state_value, state_name]
result.append(res[0])
return result
else:
return super(Experiment, self).read_group(cr, uid, domain, fields, groupby, offset=offset, limit=limit, context=context, orderby=orderby)
@api.one
def _get_version_number(self):
for exp in self:
exp.version_number = len(exp.experiment_version_ids) + 1
name = fields.Char(string="Title", required=True)
experiment_version_ids = fields.One2many('website_version.experiment.version', 'experiment_id', string="Experiment Version")
website_id = fields.Many2one('website', string="Website", required=True)
state = fields.Selection([('running', 'Running'), ('paused', 'Paused'), ('ended', 'Ended')], 'Status', required=True, copy=False, track_visibility='onchange', default='running')
goal_id = fields.Many2one('website_version.goals', string="Objective", required=True)
color = fields.Integer('Color Index')
version_number = fields.Integer(compute=_get_version_number, string='Version Number')
sequence = fields.Integer('Sequence', required=True, default=1)
google_id = fields.Char(string="Google id")
@api.model
def create(self, vals):
exp = {
'name': vals['name'],
'objectiveMetric': self.env['website_version.goals'].browse([vals['goal_id']])[0].google_ref,
'status': vals['state'],
'variations': [{'name': 'master', 'url': 'http://localhost/master'}]
}
version_list = vals.get('experiment_version_ids', [])
for version in version_list:
if version[0] == 0:
name = self.env['website_version.version'].browse([version[2]['version_id']])[0].name
#We must give a URL for each version in the experiment
exp['variations'].append({'name': name, 'url': 'http://localhost/' + name})
else:
raise Warning(_("The experiment you try to create has a bad format."))
if not version_list:
raise Warning(_("You must select at least one version in your experiment."))
vals['google_id'] = self.env['google.management'].create_an_experiment(exp, vals['website_id'])
return super(Experiment, self).create(vals)
@api.multi
def write(self, vals):
state = vals.get('state')
for exp in self:
if state and exp.state == 'ended':
raise Warning(_("You cannot modify an ended experiment."))
elif state == 'ended':
#google_data is the data to send to Googe
google_data = {
'name': exp.name,
'status': state,
'variations': [{'name': 'master', 'url': 'http://localhost/master'}],
}
for exp_v in exp.experiment_version_ids:
google_data['variations'].append({'name': exp_v.version_id.name, 'url': 'http://localhost/'+exp_v.version_id.name})
#to check the constraints before to write on the google analytics account
self.env['google.management'].update_an_experiment(google_data, exp.google_id, exp.website_id.id)
return super(Experiment, self).write(vals)
@api.multi
def unlink(self):
for exp in self:
self.env['google.management'].delete_an_experiment(exp.google_id, exp.website_id.id)
return super(Experiment, self).unlink()
@api.multi
def update_goals(self):
gm_obj = self.env['google.management']
goals_obj = self.env['website_version.goals']
website_id = self.env.context.get('website_id')
if not website_id:
raise Warning("You must specify the website.")
for goal in gm_obj.get_goal_info(website_id)[1]['items']:
if not goals_obj.search([('name', '=', goal['name'])]):
vals = {'name': goal['name'], 'google_ref': 'ga:goal' + goal['id'] + 'Completions'}
goals_obj.create(vals)
def check_no_overlap(self, version_ids):
if self.search_count(['|', ('state', '=', 'running'), ('state', '=', 'paused')]) >= 24:
return {'existing': TOO_MUCH_EXPERIMENTS, 'name': ""}
#Check if version_ids don't overlap with running experiments and return the name of the experiment if there 's an overlap
version_keys = set([v['key'] for v in self.env['ir.ui.view'].search_read([('version_id', 'in', version_ids)], ['key'])])
exp_mod = self.env['website_version.experiment']
exps = exp_mod.search([('state', '=', 'running'), ('website_id', '=', self.env.context.get('website_id'))])
for exp in exps:
for exp_ver in exp.experiment_version_ids:
for view in exp_ver.version_id.view_ids:
if view.key in version_keys:
return {'existing': OVERLAP_EXPERIMENT, 'name': exp.name}
return {'existing': CREATE_EXPERIMENT, 'name': ""}