diff options
| author | Thibault Delavallée <tde@odoo.com> | 2019-07-17 09:26:42 +0000 |
|---|---|---|
| committer | Thibault Delavallée <tde@odoo.com> | 2019-07-17 15:39:06 +0000 |
| commit | 2896aad34e797b6ca65a8efe3c5d71f31500a9a2 (patch) | |
| tree | 81f2298b04182860e84f2ca9815f313cc0c75ffc /addons/mass_mailing/models | |
| parent | 20dfba6e11ac4553509c1a3516c6453b8f8023e7 (diff) | |
[REF] mass_mailing: rename mail.mail.statistics and reporting model
PURPOSE
Mass mailing is currently a bit messy. As SMS will be added as a way to notify
people in mass through SMS let us take this opportunity to somehow clean this
application: organization, light code cleaning, model renaming.
SPECIFICATIONS
Rename mail.mail.statistics to mailing.trace and mail.statistics.report
to mail.trace.report. Rationale :
* mail.mail.statistics is linked to mail.mail model. Soon this model will
hold data related to SMS sending. It makes sense to be broader in the
naming;
* mailing.trace is more inlined with marketing.trace model that is the
marketing automation model using it in marketing automation (enterprise
application);
* mailing.trace is shorter to write;
* mail.statistics.report model should sense to be updated at the same
time;
MIGRATION
mail.mail.statistics model -> mailing.trace
mail_mail_statistics table -> mailing_trace
mail.statistics.report model -> mail.trace.report
fields updated (w column change)
* link.tracker.click: mail_stat_id -> mailing_trace_id
fields updated (no column change)
* mail.mail: statistics_ids -> mailing_trace_ids
* mail.mass_mailing: statistics_ids -> mailing_trace_ids
LINKS
Task ID 2037906
Preparing task ID 1997464 (SMS addition in mass mailing)
PR #34938
Diffstat (limited to 'addons/mass_mailing/models')
| -rw-r--r-- | addons/mass_mailing/models/__init__.py | 2 | ||||
| -rw-r--r-- | addons/mass_mailing/models/link_tracker.py | 20 | ||||
| -rw-r--r-- | addons/mass_mailing/models/mail_mail.py | 24 | ||||
| -rw-r--r-- | addons/mass_mailing/models/mail_thread.py | 20 | ||||
| -rw-r--r-- | addons/mass_mailing/models/mailing_trace.py (renamed from addons/mass_mailing/models/mass_mailing_stats.py) | 38 | ||||
| -rw-r--r-- | addons/mass_mailing/models/mass_mailing.py | 38 |
6 files changed, 70 insertions, 72 deletions
diff --git a/addons/mass_mailing/models/__init__.py b/addons/mass_mailing/models/__init__.py index afeb0313925..d00a7590cf8 100644 --- a/addons/mass_mailing/models/__init__.py +++ b/addons/mass_mailing/models/__init__.py @@ -5,7 +5,7 @@ from . import link_tracker from . import mass_mailing_contact from . import mass_mailing_list from . import mass_mailing -from . import mass_mailing_stats +from . import mailing_trace from . import mail_mail from . import mail_template from . import mail_thread diff --git a/addons/mass_mailing/models/link_tracker.py b/addons/mass_mailing/models/link_tracker.py index 2b20b007a4b..c33dae7731e 100644 --- a/addons/mass_mailing/models/link_tracker.py +++ b/addons/mass_mailing/models/link_tracker.py @@ -14,22 +14,22 @@ class LinkTracker(models.Model): class LinkTrackerClick(models.Model): _inherit = "link.tracker.click" - mail_stat_id = fields.Many2one('mail.mail.statistics', string='Mail Statistics') + mailing_trace_id = fields.Many2one('mailing.trace', string='Mail Statistics') mass_mailing_id = fields.Many2one('mail.mass_mailing', string='Mass Mailing') mass_mailing_campaign_id = fields.Many2one('mail.mass_mailing.campaign', string='Mass Mailing Campaign') def _prepare_click_values_from_route(self, **route_values): click_values = super(LinkTrackerClick, self)._prepare_click_values_from_route(**route_values) - if click_values.get('mail_stat_id'): - stat_sudo = self.env['mail.mail.statistics'].sudo().browse(route_values['mail_stat_id']).exists() - if not stat_sudo: - click_values['mail_stat_id'] = False + if click_values.get('mailing_trace_id'): + trace_sudo = self.env['mailing.trace'].sudo().browse(route_values['mailing_trace_id']).exists() + if not trace_sudo: + click_values['mailing_trace_id'] = False else: if not click_values.get('mass_mailing_campaign_id'): - click_values['mass_mailing_campaign_id'] = stat_sudo.mass_mailing_campaign_id.id + click_values['mass_mailing_campaign_id'] = trace_sudo.mass_mailing_campaign_id.id if not click_values.get('mass_mailing_id'): - click_values['mass_mailing_id'] = stat_sudo.mass_mailing_id.id + click_values['mass_mailing_id'] = trace_sudo.mass_mailing_id.id return click_values @@ -37,8 +37,8 @@ class LinkTrackerClick(models.Model): def add_click(self, code, **route_values): click = super(LinkTrackerClick, self).add_click(code, **route_values) - if click and click.mail_stat_id: - click.mail_stat_id.set_opened() - click.mail_stat_id.set_clicked() + if click and click.mailing_trace_id: + click.mailing_trace_id.set_opened() + click.mailing_trace_id.set_clicked() return click diff --git a/addons/mass_mailing/models/mail_mail.py b/addons/mass_mailing/models/mail_mail.py index a4de6be6972..1dcb643b1e7 100644 --- a/addons/mass_mailing/models/mail_mail.py +++ b/addons/mass_mailing/models/mail_mail.py @@ -14,16 +14,16 @@ class MailMail(models.Model): _inherit = ['mail.mail'] mailing_id = fields.Many2one('mail.mass_mailing', string='Mass Mailing') - statistics_ids = fields.One2many('mail.mail.statistics', 'mail_mail_id', string='Statistics') + mailing_trace_ids = fields.One2many('mailing.trace', 'mail_mail_id', string='Statistics') @api.model def create(self, values): - """ Override mail_mail creation to create an entry in mail.mail.statistics """ + """ Override mail_mail creation to create an entry in mailing.trace """ # TDE note: should be after 'all values computed', to have values (FIXME after merging other branch holding create refactoring) mail = super(MailMail, self).create(values) - if values.get('statistics_ids'): + if values.get('mailing_trace_ids'): mail_sudo = mail.sudo() - mail_sudo.statistics_ids.write({'message_id': mail_sudo.message_id, 'state': 'outgoing'}) + mail_sudo.mailing_trace_ids.write({'message_id': mail_sudo.message_id, 'state': 'outgoing'}) return mail def _get_tracking_url(self): @@ -54,12 +54,12 @@ class MailMail(models.Model): def _send_prepare_body(self): """ Override to add the tracking URL to the body and to add - Statistic_id in shorted urls """ + trace ID in shortened urls """ # TDE: temporary addition (mail was parameter) due to semi-new-API self.ensure_one() body = super(MailMail, self)._send_prepare_body() - if self.mailing_id and body and self.statistics_ids: + if self.mailing_id and body and self.mailing_trace_ids: for match in re.findall(URL_REGEX, self.body_html): href = match[0] url = match[1] @@ -67,7 +67,7 @@ class MailMail(models.Model): parsed = werkzeug.urls.url_parse(url, scheme='http') if parsed.scheme.startswith('http') and parsed.path.startswith('/r/'): - new_href = href.replace(url, url + '/m/' + str(self.statistics_ids[0].id)) + new_href = href.replace(url, url + '/m/' + str(self.mailing_trace_ids[0].id)) body = body.replace(href, new_href) # generate tracking URL @@ -96,8 +96,8 @@ class MailMail(models.Model): mail_sent = not failure_type # we consider that a recipient error is a failure with mass mailling and show them as failed for mail in self: if mail.mailing_id: - if mail_sent is True and mail.statistics_ids: - mail.statistics_ids.write({'sent': fields.Datetime.now(), 'exception': False}) - elif mail_sent is False and mail.statistics_ids: - mail.statistics_ids.write({'exception': fields.Datetime.now()}) - return super(MailMail, self)._postprocess_sent_message(success_pids, failure_reason=failure_reason, failure_type=failure_type) + if mail_sent is True and mail.mailing_trace_ids: + mail.mailing_trace_ids.write({'sent': fields.Datetime.now(), 'exception': False}) + elif mail_sent is False and mail.mailing_trace_ids: + mail.mailing_trace_ids.write({'exception': fields.Datetime.now()}) + return super(MailMail, self)._postprocess_sent_message(success_pids, failure_reason=failure_reason, failure_type=failure_type) diff --git a/addons/mass_mailing/models/mail_thread.py b/addons/mass_mailing/models/mail_thread.py index 5195672eead..28b02921801 100644 --- a/addons/mass_mailing/models/mail_thread.py +++ b/addons/mass_mailing/models/mail_thread.py @@ -13,12 +13,12 @@ BLACKLIST_MAX_BOUNCED_LIMIT = 5 class MailThread(models.AbstractModel): - """ Update MailThread to add the support of bounce management in mass mailing statistics. """ + """ Update MailThread to add the support of bounce management in mass mailing traces. """ _inherit = 'mail.thread' @api.model def message_route(self, message, message_dict, model=None, thread_id=None, custom_values=None): - """ Override to udpate mass mailing statistics based on bounce emails """ + """ Override to udpate mass mailing traces based on bounce emails """ bounce_alias = self.env['ir.config_parameter'].sudo().get_param("mail.bounce.alias") email_to = decode_message_header(message, 'To') email_to_localpart = (tools.email_split(email_to) or [''])[0].split('@', 1)[0].lower() @@ -28,19 +28,19 @@ class MailThread(models.AbstractModel): bounce_match = bounce_re.search(email_to) if bounce_match: bounced_mail_id = bounce_match.group(1) - self.env['mail.mail.statistics'].set_bounced(mail_mail_ids=[bounced_mail_id]) + self.env['mailing.trace'].set_bounced(mail_mail_ids=[bounced_mail_id]) return super(MailThread, self).message_route(message, message_dict, model, thread_id, custom_values) @api.model def message_route_process(self, message, message_dict, routes): - """ Override to update the parent mail statistics. The parent is found + """ Override to update the parent mailing traces. The parent is found by using the References header of the incoming message and looking for - matching message_id in mail.mail.statistics. """ + matching message_id in mailing.trace. """ if message.get('References') and routes: message_ids = [x.strip() for x in decode_smtp_header(message['References']).split()] - self.env['mail.mail.statistics'].set_opened(mail_message_ids=message_ids) - self.env['mail.mail.statistics'].set_replied(mail_message_ids=message_ids) + self.env['mailing.trace'].set_opened(mail_message_ids=message_ids) + self.env['mailing.trace'].set_replied(mail_message_ids=message_ids) return super(MailThread, self).message_route_process(message, message_dict, routes) def message_post_with_template(self, template_id, **kwargs): @@ -64,11 +64,11 @@ class MailThread(models.AbstractModel): super(MailThread, self)._message_receive_bounce(email, partner, mail_id=None) three_months_ago = fields.Datetime.to_string(datetime.datetime.now() - datetime.timedelta(weeks=13)) - stats = self.env['mail.mail.statistics'] \ + traces = self.env['mailing.trace'] \ .search(['&', ('bounced', '>', three_months_ago), ('email', '=ilike', email)]) \ .mapped('bounced') - if len(stats) >= BLACKLIST_MAX_BOUNCED_LIMIT: - if max(stats) > min(stats) + datetime.timedelta(weeks=1): + if len(traces) >= BLACKLIST_MAX_BOUNCED_LIMIT: + if max(traces) > min(traces) + datetime.timedelta(weeks=1): blacklist_rec = self.env['mail.blacklist'].sudo()._add(email) blacklist_rec._message_log( body='This email has been automatically blacklisted because of too much bounced.') diff --git a/addons/mass_mailing/models/mass_mailing_stats.py b/addons/mass_mailing/models/mailing_trace.py index 0710a0e8b1a..9f6556ae3ef 100644 --- a/addons/mass_mailing/models/mass_mailing_stats.py +++ b/addons/mass_mailing/models/mailing_trace.py @@ -4,14 +4,13 @@ from odoo import api, fields, models -class MailMailStats(models.Model): - """ MailMailStats models the statistics collected about emails. Those statistics +class MailingTrace(models.Model): + """ MailingTrace 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' + _name = 'mailing.trace' + _description = 'Mailing Statistics' _rec_name = 'message_id' _order = 'message_id' @@ -45,7 +44,7 @@ class MailMailStats(models.Model): replied = fields.Datetime(help='Date when this email has been replied for the first time.') bounced = fields.Datetime(help='Date when this email has bounced.') # Link tracking - links_click_ids = fields.One2many('link.tracker.click', 'mail_stat_id', string='Links click') + links_click_ids = fields.One2many('link.tracker.click', 'mailing_trace_id', string='Links click') clicked = fields.Datetime(help='Date when customer clicked on at least one tracked link') # Status state = fields.Selection(compute="_compute_state", @@ -83,7 +82,7 @@ class MailMailStats(models.Model): def create(self, values): if 'mail_mail_id' in values: values['mail_mail_id_int'] = values['mail_mail_id'] - res = super(MailMailStats, self).create(values) + res = super(MailingTrace, self).create(values) return res def _get_records(self, mail_mail_ids=None, mail_message_ids=None, domain=None): @@ -98,22 +97,21 @@ class MailMailStats(models.Model): return self.search(base_domain) def set_opened(self, mail_mail_ids=None, mail_message_ids=None): - statistics = self._get_records(mail_mail_ids, mail_message_ids, [('opened', '=', False)]) - statistics.write({'opened': fields.Datetime.now(), 'bounced': False}) - return statistics + traces = self._get_records(mail_mail_ids, mail_message_ids, [('opened', '=', False)]) + traces.write({'opened': fields.Datetime.now(), 'bounced': False}) + return traces def set_clicked(self, mail_mail_ids=None, mail_message_ids=None): - statistics = self._get_records(mail_mail_ids, mail_message_ids, [('clicked', '=', False)]) - statistics.write({'clicked': fields.Datetime.now()}) - return statistics + traces = self._get_records(mail_mail_ids, mail_message_ids, [('clicked', '=', False)]) + traces.write({'clicked': fields.Datetime.now()}) + return traces def set_replied(self, mail_mail_ids=None, mail_message_ids=None): - statistics = self._get_records(mail_mail_ids, mail_message_ids, [('replied', '=', False)]) - statistics.write({'replied': fields.Datetime.now()}) - return statistics + traces = self._get_records(mail_mail_ids, mail_message_ids, [('replied', '=', False)]) + traces.write({'replied': fields.Datetime.now()}) + return traces def set_bounced(self, mail_mail_ids=None, mail_message_ids=None): - statistics = self._get_records( - mail_mail_ids, mail_message_ids, [('bounced', '=', False), ('opened', '=', False)]) - statistics.write({'bounced': fields.Datetime.now()}) - return statistics + traces = self._get_records(mail_mail_ids, mail_message_ids, [('bounced', '=', False), ('opened', '=', False)]) + traces.write({'bounced': fields.Datetime.now()}) + return traces diff --git a/addons/mass_mailing/models/mass_mailing.py b/addons/mass_mailing/models/mass_mailing.py index 3cc86fbebd4..d0249777baf 100644 --- a/addons/mass_mailing/models/mass_mailing.py +++ b/addons/mass_mailing/models/mass_mailing.py @@ -89,7 +89,7 @@ class MassMailingCampaign(models.Model): 'the effectiveness of the mailings, without causing duplicate messages.') color = fields.Integer(string='Color Index') clicks_ratio = fields.Integer(compute="_compute_clicks_ratio", string="Number of clicks") - # stat fields + # trace statistics fields total = fields.Integer(compute="_compute_statistics") scheduled = fields.Integer(compute="_compute_statistics") failed = fields.Integer(compute="_compute_statistics") @@ -107,9 +107,9 @@ class MassMailingCampaign(models.Model): def _compute_clicks_ratio(self): self.env.cr.execute(""" - SELECT COUNT(DISTINCT(stats.id)) AS nb_mails, COUNT(DISTINCT(clicks.mail_stat_id)) AS nb_clicks, stats.mass_mailing_campaign_id AS id - FROM mail_mail_statistics AS stats - LEFT OUTER JOIN link_tracker_click AS clicks ON clicks.mail_stat_id = stats.id + SELECT COUNT(DISTINCT(stats.id)) AS nb_mails, COUNT(DISTINCT(clicks.mailing_trace_id)) AS nb_clicks, stats.mass_mailing_campaign_id AS id + FROM mailing_trace AS stats + LEFT OUTER JOIN link_tracker_click AS clicks ON clicks.mailing_trace_id = stats.id WHERE stats.mass_mailing_campaign_id IN %s GROUP BY stats.mass_mailing_campaign_id """, (tuple(self.ids), )) @@ -134,7 +134,7 @@ class MassMailingCampaign(models.Model): COUNT(CASE WHEN s.replied is not null THEN 1 ELSE null END) AS replied , COUNT(CASE WHEN s.bounced is not null THEN 1 ELSE null END) AS bounced FROM - mail_mail_statistics s + mailing_trace s RIGHT JOIN mail_mass_mailing_campaign c ON (c.id = s.mass_mailing_campaign_id) @@ -169,7 +169,7 @@ class MassMailingCampaign(models.Model): domain = [('mass_mailing_campaign_id', '=', campaign.id)] if model: domain += [('model', '=', model)] - res[campaign.id] = set(self.env['mail.mail.statistics'].search(domain).mapped('res_id')) + res[campaign.id] = set(self.env['mailing.trace'].search(domain).mapped('res_id')) return res @api.model @@ -253,7 +253,7 @@ class MassMailing(models.Model): contact_ab_pc = fields.Integer(string='A/B Testing percentage', help='Percentage of the contacts that will be mailed. Recipients will be taken randomly.', default=100) # statistics data - statistics_ids = fields.One2many('mail.mail.statistics', 'mass_mailing_id', string='Emails Statistics') + mailing_trace_ids = fields.One2many('mailing.trace', 'mass_mailing_id', string='Emails Statistics') total = fields.Integer(compute="_compute_total") scheduled = fields.Integer(compute="_compute_statistics") expected = fields.Integer(compute="_compute_statistics") @@ -277,9 +277,9 @@ class MassMailing(models.Model): def _compute_clicks_ratio(self): self.env.cr.execute(""" - SELECT COUNT(DISTINCT(stats.id)) AS nb_mails, COUNT(DISTINCT(clicks.mail_stat_id)) AS nb_clicks, stats.mass_mailing_id AS id - FROM mail_mail_statistics AS stats - LEFT OUTER JOIN link_tracker_click AS clicks ON clicks.mail_stat_id = stats.id + SELECT COUNT(DISTINCT(stats.id)) AS nb_mails, COUNT(DISTINCT(clicks.mailing_trace_id)) AS nb_clicks, stats.mass_mailing_id AS id + FROM mailing_trace AS stats + LEFT OUTER JOIN link_tracker_click AS clicks ON clicks.mailing_trace_id = stats.id WHERE stats.mass_mailing_id IN %s GROUP BY stats.mass_mailing_id """, (tuple(self.ids), )) @@ -311,7 +311,7 @@ class MassMailing(models.Model): COUNT(CASE WHEN s.bounced is not null THEN 1 ELSE null END) AS bounced, COUNT(CASE WHEN s.exception is not null THEN 1 ELSE null END) AS failed FROM - mail_mail_statistics s + mailing_trace s RIGHT JOIN mail_mass_mailing m ON (m.id = s.mass_mailing_id) @@ -441,10 +441,10 @@ class MassMailing(models.Model): def retry_failed_mail(self): failed_mails = self.env['mail.mail'].search([('mailing_id', 'in', self.ids), ('state', '=', 'exception')]) - failed_mails.mapped('statistics_ids').unlink() + failed_mails.mapped('mailing_trace_ids').unlink() failed_mails.sudo().unlink() res_ids = self._get_recipients() - except_mailed = self.env['mail.mail.statistics'].search([ + except_mailed = self.env['mailing.trace'].search([ ('model', '=', self.mailing_model_real), ('res_id', 'in', res_ids), ('exception', '!=', False), @@ -471,11 +471,11 @@ class MassMailing(models.Model): def _action_view_documents_filtered(self, view_filter): if view_filter in ('sent', 'opened', 'replied', 'bounced', 'clicked'): - opened_stats = self.statistics_ids.filtered(lambda stat: stat[view_filter]) + opened_stats = self.mailing_trace_ids.filtered(lambda stat: stat[view_filter]) elif view_filter == ('delivered'): - opened_stats = self.statistics_ids.filtered(lambda stat: stat.sent and not stat.bounced) + opened_stats = self.mailing_trace_ids.filtered(lambda stat: stat.sent and not stat.bounced) else: - opened_stats = self.env['mail.mail.statistics'] + opened_stats = self.env['mailing.trace'] res_ids = opened_stats.mapped('res_id') model_name = self.env['ir.model']._get(self.mailing_model_real).display_name return { @@ -551,7 +551,7 @@ class MassMailing(models.Model): # + use a basic heuristic for extracting emails query = """ SELECT lower(substring(t.%(mail_field)s, '([^ ,;<@]+@[^> ,;]+)')) - FROM mail_mail_statistics s + FROM mailing_trace s JOIN %(target)s t ON (s.res_id = t.id) WHERE substring(t.%(mail_field)s, '([^ ,;<@]+@[^> ,;]+)') IS NOT NULL """ @@ -561,7 +561,7 @@ class MassMailing(models.Model): mail_field = 'email' query = """ SELECT lower(substring(p.%(mail_field)s, '([^ ,;<@]+@[^> ,;]+)')) - FROM mail_mail_statistics s + FROM mailing_trace s JOIN %(target)s t ON (s.res_id = t.id) JOIN res_partner p ON (t.partner_id = p.id) WHERE substring(p.%(mail_field)s, '([^ ,;<@]+@[^> ,;]+)') IS NOT NULL @@ -626,7 +626,7 @@ class MassMailing(models.Model): def _get_remaining_recipients(self): res_ids = self._get_recipients() - already_mailed = self.env['mail.mail.statistics'].search_read([ + already_mailed = self.env['mailing.trace'].search_read([ ('model', '=', self.mailing_model_real), ('res_id', 'in', res_ids), ('mass_mailing_id', '=', self.id)], ['res_id']) |
