[IMP] mass_mailing: now allowing to mail only a part of

the mailing lists, to perform AB testing. Also allows to avoid mailing contacts
more than once in a given campaign.

bzr revid: tde@openerp.com-20140401132454-g65utzd1zd8crpq9
This commit is contained in:
Thibault Delavallée 2014-04-01 15:24:54 +02:00
parent 1dc762b7c0
commit d5bc161438
2 changed files with 80 additions and 14 deletions

View File

@ -21,6 +21,7 @@
from datetime import datetime
from dateutil import relativedelta
import random
import urllib
import urlparse
@ -69,9 +70,6 @@ class MassMailingContact(osv.Model):
rec_id = self.create(cr, uid, {'name': name, 'email': email}, context=context)
return self.name_get(cr, uid, [rec_id], context)[0]
def name_get(self, cr, uid, ids, context=None):
return [(contact.id, '%s <%s>' % (contact.name, contact.email)) for contact in self.browse(cr, uid, ids, context=context)]
class MassMailingList(osv.Model):
"""Model of a contact list. """
@ -257,10 +255,11 @@ class MassMailingCampaign(osv.Model):
'mail.mass_mailing', 'mass_mailing_campaign_id',
'Mass Mailings',
),
'statistics_ids': fields.one2many(
'mail.mail.statistics', 'mass_mailing_campaign_id',
'Sent Emails',
),
'ab_testing': fields.boolean(
'AB Testing',
help='If checked, recipients will be mailed only once, allowing to send'
'various mailings in a single campaign to test the effectiveness'
'of the mailings.'),
'color': fields.integer('Color Index'),
# stat fields
'total': fields.function(
@ -338,6 +337,23 @@ class MassMailingCampaign(osv.Model):
'context': dict(context, default_mass_mailing_campaign_id=ids[0]),
}
#------------------------------------------------------
# API
#------------------------------------------------------
def get_recipients(self, cr, uid, ids, model=None, context=None):
"""Return the recipints of a mailing campaign. This is based on the statistics
build for each mailing. """
Statistics = self.pool['mail.mail.statistics']
res = dict.fromkeys(ids, False)
for cid in ids:
domain = [('mass_mailing_campaign_id', '=', cid)]
if model:
domain += [('model', '=', model)]
stat_ids = Statistics.search(cr, uid, domain, context=context)
res[cid] = set(stat.res_id for stat in Statistics.browse(cr, uid, stat_ids, context=context))
return res
class MassMailing(osv.Model):
""" MassMailing models a wave of emails for a mass mailign campaign.
@ -416,13 +432,18 @@ class MassMailing(osv.Model):
return results
def _get_contact_nbr(self, cr, uid, ids, name, arg, context=None):
res = dict.fromkeys(ids, 0)
res = dict.fromkeys(ids, False)
for mailing in self.browse(cr, uid, ids, context=context):
res[mailing.id] = self.pool[mailing.mailing_model].search(
val = {'contact_nbr': 0, 'contact_ab_nbr': 0, 'contact_ab_done': 0}
val['contact_nbr'] = self.pool[mailing.mailing_model].search(
cr, uid,
self.pool['mail.mass_mailing.list'].get_global_domain(cr, uid, [c.id for c in mailing.contact_list_ids], context=context)[mailing.mailing_model],
count=True, context=context
)
val['contact_ab_nbr'] = int(val['contact_nbr'] * mailing.contact_ab_pc / 100.0)
if mailing.mass_mailing_campaign_id and mailing.ab_testing:
val['contact_ab_done'] = len(self.pool['mail.mass_mailing.campaign'].get_recipients(cr, uid, [mailing.mass_mailing_campaign_id.id], context=context)[mailing.mass_mailing_campaign_id.id])
res[mailing.id] = val
return res
def _get_mailing_model(self, cr, uid, context=None):
@ -457,6 +478,10 @@ class MassMailing(osv.Model):
'mail.mass_mailing.campaign', 'Mass Mailing Campaign',
ondelete='set null',
),
'ab_testing': fields.related(
'mass_mailing_campaign_id', 'ab_testing',
type='boolean', string='AB Testing'
),
'color': fields.related(
'mass_mailing_campaign_id', 'color',
type='integer', string='Color Index',
@ -471,7 +496,22 @@ class MassMailing(osv.Model):
string='Mailing Lists',
domain="[('model', '=', mailing_model)]",
),
'contact_nbr': fields.function(_get_contact_nbr, type='integer', string='Contact Number'),
'contact_nbr': fields.function(
_get_contact_nbr, type='integer', multi='_get_contact_nbr',
string='Contact Number'
),
'contact_ab_pc': fields.integer(
'AB Testing percentage',
help='Percentage of the contacts that will be mailed. Recipients will be taken randomly.'
),
'contact_ab_nbr': fields.function(
_get_contact_nbr, type='integer', multi='_get_contact_nbr',
string='Contact Number in AB Testing'
),
'contact_ab_done': fields.function(
_get_contact_nbr, type='integer', multi='_get_contact_nbr',
string='Number of already mailed contacts'
),
# statistics data
'statistics_ids': fields.one2many(
'mail.mail.statistics', 'mass_mailing_id',
@ -535,6 +575,7 @@ class MassMailing(osv.Model):
'date': fields.datetime.now,
'email_from': lambda self, cr, uid, ctx=None: self.pool['mail.message']._get_default_from(cr, uid, context=ctx),
'mailing_model': 'res.partner',
'contact_ab_pc': 100,
}
#------------------------------------------------------
@ -654,6 +695,25 @@ class MassMailing(osv.Model):
partners = self.pool['res.partner'].browse(cr, uid, res_ids, context=context)
return dict((partner.id, {'partner_id': partner.id, 'name': partner.name, 'email': partner.email}) for partner in partners)
def get_recipients(self, cr, uid, mailing, context=None):
domain = self.pool['mail.mass_mailing.list'].get_global_domain(
cr, uid, [l.id for l in mailing.contact_list_ids], context=context
)[mailing.mailing_model]
res_ids = self.pool[mailing.mailing_model].search(cr, uid, domain, context=context)
# randomly choose a fragment
if mailing.contact_ab_pc != 100:
topick = mailing.contact_ab_nbr
if mailing.mass_mailing_campaign_id and mailing.ab_testing:
already_mailed = self.pool['mail.mass_mailing.campaign'].get_recipients(cr, uid, [mailing.mass_mailing_campaign_id.id], context=context)[mailing.mass_mailing_campaign_id.id]
else:
already_mailed = set([])
remaining = set(res_ids).difference(already_mailed)
if topick > len(remaining):
topick = len(remaining)
res_ids = random.sample(remaining, topick)
return res_ids
def get_unsubscribe_url(self, cr, uid, mailing_id, res_id, email, msg=None, context=None):
base_url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url')
url = urlparse.urljoin(
@ -674,10 +734,7 @@ class MassMailing(osv.Model):
raise Warning('Please select recipients.')
# get mail and recipints data
domain = self.pool['mail.mass_mailing.list'].get_global_domain(
cr, uid, [l.id for l in mailing.contact_list_ids], context=context
)[mailing.mailing_model]
res_ids = self.pool[mailing.mailing_model].search(cr, uid, domain, context=context)
res_ids = self.get_recipients(cr, uid, mailing, context=context)
template_values = self.pool['mail.compose.message'].generate_email_for_composer_batch(
cr, uid, mailing.template_id.id, res_ids,
context=context, fields=['body_html', 'attachment_ids', 'mail_server_id'])

View File

@ -275,6 +275,14 @@
<field name="contact_nbr" nolabel="1" class="oe_inline" readonly="True"/> contacts selected
<button name="action_see_recipients" type="object" string="See Recipients" class="oe_link" style='margin-left: 8px;'/>
</div>
<label for="contact_ab_pc" string="AB Testing"
groups="mass_mailing.group_mass_mailing_campaign"/>
<div groups="mass_mailing.group_mass_mailing_campaign">
<field name="ab_testing" invisible="1"/>
<field name="contact_ab_pc" class="oe_inline"/>%, <field name="contact_ab_nbr" class="oe_inline"/> contacts to mail<br />
<field name="contact_ab_done" class="oe_inline"
attrs="{'invisible': [('ab_testing', '=', False)]}"/> already mailed
</div>
</group>
</div>
</group>
@ -423,6 +431,7 @@
<field name="name"/>
<field name="user_id"/>
<field name="category_id"/>
<field name="ab_testing"/>
</group>
<group>
here be some graphs