[IMP] Adding stats file
bzr revid: fp@tinyerp.com-20140412232548-csyb7j13fyu4cwnw
This commit is contained in:
parent
e9cf53934b
commit
227b9a917d
|
@ -0,0 +1,350 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2013-today OpenERP SA (<http://www.openerp.com>)
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from datetime import datetime
|
||||
from dateutil import relativedelta
|
||||
import random
|
||||
try:
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
import json
|
||||
import urllib
|
||||
import urlparse
|
||||
|
||||
from openerp import tools
|
||||
from openerp.exceptions import Warning
|
||||
from openerp.tools.safe_eval import safe_eval as eval
|
||||
from openerp.tools.translate import _
|
||||
from openerp.osv import osv, fields
|
||||
|
||||
class MassMailingList(osv.Model):
|
||||
_inherit = 'mail.mass_mailing.list'
|
||||
def _get_contact_nbr(self, cr, uid, ids, name, arg, context=None):
|
||||
result = dict.fromkeys(ids, 0)
|
||||
mlc = self.pool.get('mail.mass_mailing.contact')
|
||||
for m in mlc.read_group(cr, uid, [('list_id','in',ids)], ['list_id'], ['list_id'], context=context):
|
||||
result[m['list_id'][0]] = m['list_id_count']
|
||||
return result
|
||||
|
||||
_columns = {
|
||||
'contact_nbr': fields.function(
|
||||
_get_contact_nbr, type='integer',
|
||||
string='Number of Contacts',
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class MassMailingCampaign(osv.Model):
|
||||
_inherit = "mail.mass_mailing.campaign"
|
||||
def _get_statistics(self, cr, uid, ids, name, arg, context=None):
|
||||
""" Compute statistics of the mass mailing campaign """
|
||||
Statistics = self.pool['mail.mail.statistics']
|
||||
results = dict.fromkeys(ids, False)
|
||||
for cid in ids:
|
||||
stat_ids = Statistics.search(cr, uid, [('mass_mailing_campaign_id', '=', cid)], context=context)
|
||||
stats = Statistics.browse(cr, uid, stat_ids, context=context)
|
||||
results[cid] = {
|
||||
'total': len(stats),
|
||||
'failed': len([s for s in stats if not s.scheduled is False and s.sent is False and not s.exception is False]),
|
||||
'scheduled': len([s for s in stats if not s.scheduled is False and s.sent is False and s.exception is False]),
|
||||
'sent': len([s for s in stats if not s.sent is False]),
|
||||
'opened': len([s for s in stats if not s.opened is False]),
|
||||
'replied': len([s for s in stats if not s.replied is False]),
|
||||
'bounced': len([s for s in stats if not s.bounced is False]),
|
||||
}
|
||||
results[cid]['delivered'] = results[cid]['sent'] - results[cid]['bounced']
|
||||
results[cid]['received_ratio'] = 100.0 * results[cid]['delivered'] / (results[cid]['sent'] or 1)
|
||||
results[cid]['opened_ratio'] = 100.0 * results[cid]['opened'] / (results[cid]['sent'] or 1)
|
||||
results[cid]['replied_ratio'] = 100.0 * results[cid]['replied'] / (results[cid]['sent'] or 1)
|
||||
return results
|
||||
|
||||
_columns = {
|
||||
'total': fields.function(
|
||||
_get_statistics, string='Total',
|
||||
type='integer', multi='_get_statistics'
|
||||
),
|
||||
'scheduled': fields.function(
|
||||
_get_statistics, string='Scheduled',
|
||||
type='integer', multi='_get_statistics'
|
||||
),
|
||||
'failed': fields.function(
|
||||
_get_statistics, string='Failed',
|
||||
type='integer', multi='_get_statistics'
|
||||
),
|
||||
'sent': fields.function(
|
||||
_get_statistics, string='Sent Emails',
|
||||
type='integer', multi='_get_statistics'
|
||||
),
|
||||
'delivered': fields.function(
|
||||
_get_statistics, string='Delivered',
|
||||
type='integer', multi='_get_statistics',
|
||||
),
|
||||
'opened': fields.function(
|
||||
_get_statistics, string='Opened',
|
||||
type='integer', multi='_get_statistics',
|
||||
),
|
||||
'replied': fields.function(
|
||||
_get_statistics, string='Replied',
|
||||
type='integer', multi='_get_statistics'
|
||||
),
|
||||
'bounced': fields.function(
|
||||
_get_statistics, string='Bounced',
|
||||
type='integer', multi='_get_statistics'
|
||||
),
|
||||
'received_ratio': fields.function(
|
||||
_get_statistics, string='Received Ratio',
|
||||
type='integer', multi='_get_statistics',
|
||||
),
|
||||
'opened_ratio': fields.function(
|
||||
_get_statistics, string='Opened Ratio',
|
||||
type='integer', multi='_get_statistics',
|
||||
),
|
||||
'replied_ratio': fields.function(
|
||||
_get_statistics, string='Replied Ratio',
|
||||
type='integer', multi='_get_statistics',
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class MassMailing(osv.Model):
|
||||
_inherit = 'mail.mass_mailing'
|
||||
_period_number = 6
|
||||
def __get_bar_values(self, cr, uid, id, obj, domain, read_fields, value_field, groupby_field, context=None):
|
||||
""" Generic method to generate data for bar chart values using SparklineBarWidget.
|
||||
This method performs obj.read_group(cr, uid, domain, read_fields, groupby_field).
|
||||
|
||||
:param obj: the target model (i.e. crm_lead)
|
||||
:param domain: the domain applied to the read_group
|
||||
:param list read_fields: the list of fields to read in the read_group
|
||||
:param str value_field: the field used to compute the value of the bar slice
|
||||
:param str groupby_field: the fields used to group
|
||||
|
||||
:return list section_result: a list of dicts: [
|
||||
{ 'value': (int) bar_column_value,
|
||||
'tootip': (str) bar_column_tooltip,
|
||||
}
|
||||
]
|
||||
"""
|
||||
date_begin = datetime.strptime(self.browse(cr, uid, id, context=context).date, tools.DEFAULT_SERVER_DATETIME_FORMAT).date()
|
||||
section_result = [{'value': 0,
|
||||
'tooltip': (date_begin + relativedelta.relativedelta(days=i)).strftime('%d %B %Y'),
|
||||
} for i in range(0, self._period_number)]
|
||||
group_obj = obj.read_group(cr, uid, domain, read_fields, groupby_field, context=context)
|
||||
field_col_info = obj._all_columns.get(groupby_field.split(':')[0])
|
||||
pattern = tools.DEFAULT_SERVER_DATE_FORMAT if field_col_info.column._type == 'date' else tools.DEFAULT_SERVER_DATETIME_FORMAT
|
||||
for group in group_obj:
|
||||
group_begin_date = datetime.strptime(group['__domain'][0][2], pattern).date()
|
||||
timedelta = relativedelta.relativedelta(group_begin_date, date_begin)
|
||||
section_result[timedelta.days] = {'value': group.get(value_field, 0), 'tooltip': group.get(groupby_field)}
|
||||
return section_result
|
||||
|
||||
def _get_daily_statistics(self, cr, uid, ids, field_name, arg, context=None):
|
||||
""" Get the daily statistics of the mass mailing. This is done by a grouping
|
||||
on opened and replied fields. Using custom format in context, we obtain
|
||||
results for the next 6 days following the mass mailing date. """
|
||||
obj = self.pool['mail.mail.statistics']
|
||||
res = {}
|
||||
for id in ids:
|
||||
res[id] = {}
|
||||
date_begin = datetime.strptime(self.browse(cr, uid, id, context=context).date, tools.DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
date_end = date_begin + relativedelta.relativedelta(days=self._period_number - 1)
|
||||
date_begin_str = date_begin.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
date_end_str = date_end.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
domain = [('mass_mailing_id', '=', id), ('opened', '>=', date_begin_str), ('opened', '<=', date_end_str)]
|
||||
res[id]['opened_dayly'] = json.dumps(self.__get_bar_values(cr, uid, id, obj, domain, ['opened'], 'opened_count', 'opened:day', context=context))
|
||||
domain = [('mass_mailing_id', '=', id), ('replied', '>=', date_begin_str), ('replied', '<=', date_end_str)]
|
||||
res[id]['replied_dayly'] = json.dumps(self.__get_bar_values(cr, uid, id, obj, domain, ['replied'], 'replied_count', 'replied:day', context=context))
|
||||
return res
|
||||
|
||||
def _get_statistics(self, cr, uid, ids, name, arg, context=None):
|
||||
""" Compute statistics of the mass mailing campaign """
|
||||
Statistics = self.pool['mail.mail.statistics']
|
||||
results = dict.fromkeys(ids, False)
|
||||
for mid in ids:
|
||||
stat_ids = Statistics.search(cr, uid, [('mass_mailing_id', '=', mid)], context=context)
|
||||
stats = Statistics.browse(cr, uid, stat_ids, context=context)
|
||||
results[mid] = {
|
||||
'total': len(stats),
|
||||
'failed': len([s for s in stats if not s.scheduled is False and s.sent is False and not s.exception is False]),
|
||||
'scheduled': len([s for s in stats if not s.scheduled is False and s.sent is False and s.exception is False]),
|
||||
'sent': len([s for s in stats if not s.sent is False]),
|
||||
'opened': len([s for s in stats if not s.opened is False]),
|
||||
'replied': len([s for s in stats if not s.replied is False]),
|
||||
'bounced': len([s for s in stats if not s.bounced is False]),
|
||||
}
|
||||
results[mid]['delivered'] = results[mid]['sent'] - results[mid]['bounced']
|
||||
results[mid]['received_ratio'] = 100.0 * results[mid]['delivered'] / (results[mid]['sent'] or 1)
|
||||
results[mid]['opened_ratio'] = 100.0 * results[mid]['opened'] / (results[mid]['sent'] or 1)
|
||||
results[mid]['replied_ratio'] = 100.0 * results[mid]['replied'] / (results[mid]['sent'] or 1)
|
||||
return results
|
||||
|
||||
# To improve
|
||||
def _get_contact_nbr(self, cr, uid, ids, name, arg, context=None):
|
||||
res = dict.fromkeys(ids, False)
|
||||
for mailing in self.browse(cr, uid, ids, context=context):
|
||||
if not mailing.mailing_domain:
|
||||
res[mailing.id] = 0
|
||||
continue
|
||||
print mailing.mailing_model, mailing.mailing_domain
|
||||
res[mailing.id] = self.pool[mailing.mailing_model].search(
|
||||
cr, uid, eval(mailing.mailing_domain), count=True, context=context
|
||||
)
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
'contact_nbr': fields.function(_get_contact_nbr, type='integer', string='Contact Number'),
|
||||
# statistics data
|
||||
'statistics_ids': fields.one2many(
|
||||
'mail.mail.statistics', 'mass_mailing_id',
|
||||
'Emails Statistics',
|
||||
),
|
||||
'total': fields.function(
|
||||
_get_statistics, string='Total',
|
||||
type='integer', multi='_get_statistics',
|
||||
),
|
||||
'scheduled': fields.function(
|
||||
_get_statistics, string='Scheduled',
|
||||
type='integer', multi='_get_statistics',
|
||||
),
|
||||
'failed': fields.function(
|
||||
_get_statistics, string='Failed',
|
||||
type='integer', multi='_get_statistics',
|
||||
),
|
||||
'sent': fields.function(
|
||||
_get_statistics, string='Sent',
|
||||
type='integer', multi='_get_statistics',
|
||||
),
|
||||
'delivered': fields.function(
|
||||
_get_statistics, string='Delivered',
|
||||
type='integer', multi='_get_statistics',
|
||||
),
|
||||
'opened': fields.function(
|
||||
_get_statistics, string='Opened',
|
||||
type='integer', multi='_get_statistics',
|
||||
),
|
||||
'replied': fields.function(
|
||||
_get_statistics, string='Replied',
|
||||
type='integer', multi='_get_statistics',
|
||||
),
|
||||
'bounced': fields.function(
|
||||
_get_statistics, string='Bounced',
|
||||
type='integer', multi='_get_statistics',
|
||||
),
|
||||
'received_ratio': fields.function(
|
||||
_get_statistics, string='Received Ratio',
|
||||
type='integer', multi='_get_statistics',
|
||||
),
|
||||
'opened_ratio': fields.function(
|
||||
_get_statistics, string='Opened Ratio',
|
||||
type='integer', multi='_get_statistics',
|
||||
),
|
||||
'replied_ratio': fields.function(
|
||||
_get_statistics, string='Replied Ratio',
|
||||
type='integer', multi='_get_statistics',
|
||||
),
|
||||
# dayly ratio
|
||||
'opened_dayly': fields.function(
|
||||
_get_daily_statistics, string='Opened',
|
||||
type='char', multi='_get_daily_statistics',
|
||||
oldname='opened_monthly',
|
||||
),
|
||||
'replied_dayly': fields.function(
|
||||
_get_daily_statistics, string='Replied',
|
||||
type='char', multi='_get_daily_statistics',
|
||||
oldname='replied_monthly',
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
# Merge this on emails
|
||||
class MailMailStats(osv.Model):
|
||||
""" MailMailStats models the statistics collected about emails. Those statistics
|
||||
are stored in a separated model and table to avoid bloating the mail_mail table
|
||||
with statistics values. This also allows to delete emails send with mass mailing
|
||||
without loosing the statistics about them. """
|
||||
|
||||
_name = 'mail.mail.statistics'
|
||||
_description = 'Email Statistics'
|
||||
_rec_name = 'message_id'
|
||||
_order = 'message_id'
|
||||
|
||||
_columns = {
|
||||
'mail_mail_id': fields.integer(
|
||||
'Mail ID',
|
||||
help='ID of the related mail_mail. This field is an integer field because'
|
||||
'the related mail_mail can be deleted separately from its statistics.'
|
||||
),
|
||||
'message_id': fields.char('Message-ID'),
|
||||
'model': fields.char('Document model'),
|
||||
'res_id': fields.integer('Document ID'),
|
||||
# campaign / wave data
|
||||
'mass_mailing_id': fields.many2one(
|
||||
'mail.mass_mailing', 'Mass Mailing',
|
||||
ondelete='set null',
|
||||
),
|
||||
'mass_mailing_campaign_id': fields.related(
|
||||
'mass_mailing_id', 'mass_mailing_campaign_id',
|
||||
type='many2one', ondelete='set null',
|
||||
relation='mail.mass_mailing.campaign',
|
||||
string='Mass Mailing Campaign',
|
||||
store=True, readonly=True,
|
||||
),
|
||||
# Bounce and tracking
|
||||
'scheduled': fields.datetime('Scheduled', help='Date when the email has been created'),
|
||||
'sent': fields.datetime('Sent', help='Date when the email has been sent'),
|
||||
'exception': fields.datetime('Exception', help='Date of technical error leading to the email not being sent'),
|
||||
'opened': fields.datetime('Opened', help='Date when the email has been opened the first time'),
|
||||
'replied': fields.datetime('Replied', help='Date when this email has been replied for the first time.'),
|
||||
'bounced': fields.datetime('Bounced', help='Date when this email has bounced.'),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'scheduled': fields.datetime.now,
|
||||
}
|
||||
|
||||
def _get_ids(self, cr, uid, ids=None, mail_mail_ids=None, mail_message_ids=None, domain=None, context=None):
|
||||
if not ids and mail_mail_ids:
|
||||
base_domain = [('mail_mail_id', 'in', mail_mail_ids)]
|
||||
elif not ids and mail_message_ids:
|
||||
base_domain = [('message_id', 'in', mail_message_ids)]
|
||||
else:
|
||||
base_domain = [('id', 'in', ids or [])]
|
||||
if domain:
|
||||
base_domain = ['&'] + domain + base_domain
|
||||
return self.search(cr, uid, base_domain, context=context)
|
||||
|
||||
def set_opened(self, cr, uid, ids=None, mail_mail_ids=None, mail_message_ids=None, context=None):
|
||||
stat_ids = self._get_ids(cr, uid, ids, mail_mail_ids, mail_message_ids, [('opened', '=', False)], context)
|
||||
self.write(cr, uid, stat_ids, {'opened': fields.datetime.now()}, context=context)
|
||||
return stat_ids
|
||||
|
||||
def set_replied(self, cr, uid, ids=None, mail_mail_ids=None, mail_message_ids=None, context=None):
|
||||
stat_ids = self._get_ids(cr, uid, ids, mail_mail_ids, mail_message_ids, [('replied', '=', False)], context)
|
||||
self.write(cr, uid, stat_ids, {'replied': fields.datetime.now()}, context=context)
|
||||
return stat_ids
|
||||
|
||||
def set_bounced(self, cr, uid, ids=None, mail_mail_ids=None, mail_message_ids=None, context=None):
|
||||
stat_ids = self._get_ids(cr, uid, ids, mail_mail_ids, mail_message_ids, [('bounced', '=', False)], context)
|
||||
self.write(cr, uid, stat_ids, {'bounced': fields.datetime.now()}, context=context)
|
||||
return stat_ids
|
||||
|
|
@ -185,13 +185,13 @@
|
|||
class="oe_highlight" string="Send to All"/>
|
||||
<field name="state" widget="statusbar"/>
|
||||
</header>
|
||||
<div class="oe_form_box_info oe_text_center" attrs="{'invisible': [('scheduled', '=', 0)]}">
|
||||
<p><strong>
|
||||
<field name="scheduled" class="oe_inline"/>emails are in queue
|
||||
and will be sent soon.
|
||||
</strong></p>
|
||||
</div>
|
||||
<sheet>
|
||||
<div class="oe_form_box_info oe_text_center" attrs="{'invisible': [('scheduled', '=', 0)]}">
|
||||
<p>
|
||||
<strong><field name="scheduled" class="oe_inline"/>emails are in queue
|
||||
and will be sent soon.</strong>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="oe_button_box pull-right" attrs="{'invisible': [('total', '=', 0)]}">
|
||||
<button name="%(action_mail_mass_mailing_report)d"
|
||||
|
|
Loading…
Reference in New Issue