diff --git a/mail_tracking/README.rst b/mail_tracking/README.rst index 40ba269ccd..3eb00cc7b6 100644 --- a/mail_tracking/README.rst +++ b/mail_tracking/README.rst @@ -63,7 +63,7 @@ These are all available status icons: .. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/205/9.0 + :target: https://runbot.odoo-community.org/runbot/205/10.0 If you want to see all tracking emails and events you can go to diff --git a/mail_tracking/__manifest__.py b/mail_tracking/__manifest__.py index 68ae3b9ac1..2581eab133 100644 --- a/mail_tracking/__manifest__.py +++ b/mail_tracking/__manifest__.py @@ -5,20 +5,21 @@ { "name": "Email tracking", "summary": "Email tracking system for all mails sent", - "version": "9.0.1.0.0", + "version": "10.0.1.0.0", "category": "Social Network", "website": "http://www.tecnativa.com", "author": "Tecnativa, " "Odoo Community Association (OCA)", "license": "AGPL-3", "application": False, - 'installable': False, + 'installable': True, "depends": [ "decimal_precision", "mail", ], "data": [ "data/tracking_data.xml", + "security/mail_tracking_email_security.xml", "security/ir.model.access.csv", "views/assets.xml", "views/mail_tracking_email_view.xml", diff --git a/mail_tracking/controllers/main.py b/mail_tracking/controllers/main.py index da14949d69..9be9730ce7 100644 --- a/mail_tracking/controllers/main.py +++ b/mail_tracking/controllers/main.py @@ -4,24 +4,37 @@ import werkzeug from psycopg2 import OperationalError -from openerp import api, http, registry, SUPERUSER_ID +from odoo import api, http, registry, SUPERUSER_ID import logging _logger = logging.getLogger(__name__) BLANK = 'R0lGODlhAQABAIAAANvf7wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==' -def _env_get(db): +def _env_get(db, callback, tracking_id, event_type, **kw): + res = 'NOT FOUND' reg = False - try: - reg = registry(db) - except OperationalError: - _logger.warning("Selected BD '%s' not found", db) - except: # pragma: no cover - _logger.warning("Selected BD '%s' connection error", db) - if reg: - return api.Environment(reg.cursor(), SUPERUSER_ID, {}) - return False + current = http.request.db and db == http.request.db + env = current and http.request.env + if not env: + with api.Environment.manage(): + try: + reg = registry(db) + except OperationalError: + _logger.warning("Selected BD '%s' not found", db) + except: # pragma: no cover + _logger.warning("Selected BD '%s' connection error", db) + if reg: + _logger.info("New environment for database '%s'", db) + with reg.cursor() as new_cr: + new_env = api.Environment(new_cr, SUPERUSER_ID, {}) + res = callback(new_env, tracking_id, event_type, **kw) + new_env.cr.commit() + else: + # make sudo when reusing environment + env = env(user=SUPERUSER_ID) + res = callback(env, tracking_id, event_type, **kw) + return res class MailTrackingController(http.Controller): @@ -35,49 +48,37 @@ def _request_metadata(self): 'ua_family': request.user_agent.browser or False, } + def _tracking_open(self, env, tracking_id, event_type, **kw): + tracking_email = env['mail.tracking.email'].search([ + ('id', '=', tracking_id), + ]) + if tracking_email: + metadata = self._request_metadata() + tracking_email.event_create('open', metadata) + else: + _logger.warning( + "MailTracking email '%s' not found", tracking_id) + + def _tracking_event(self, env, tracking_id, event_type, **kw): + metadata = self._request_metadata() + return env['mail.tracking.email'].event_process( + http.request, kw, metadata, event_type=event_type) + @http.route('/mail/tracking/all/', type='http', auth='none', csrf=False) def mail_tracking_all(self, db, **kw): - env = _env_get(db) - if not env: - return 'NOT FOUND' - metadata = self._request_metadata() - response = env['mail.tracking.email'].event_process( - http.request, kw, metadata) - env.cr.commit() - env.cr.close() - return response + return _env_get(db, self._tracking_event, None, None, **kw) @http.route('/mail/tracking/event//', type='http', auth='none', csrf=False) def mail_tracking_event(self, db, event_type, **kw): - env = _env_get(db) - if not env: - return 'NOT FOUND' - metadata = self._request_metadata() - response = env['mail.tracking.email'].event_process( - http.request, kw, metadata, event_type=event_type) - env.cr.commit() - env.cr.close() - return response + return _env_get(db, self._tracking_event, None, event_type, **kw) @http.route('/mail/tracking/open/' '//blank.gif', type='http', auth='none') def mail_tracking_open(self, db, tracking_email_id, **kw): - env = _env_get(db) - if env: - tracking_email = env['mail.tracking.email'].search([ - ('id', '=', tracking_email_id), - ]) - if tracking_email: - metadata = self._request_metadata() - tracking_email.event_create('open', metadata) - else: - _logger.warning( - "MailTracking email '%s' not found", tracking_email_id) - env.cr.commit() - env.cr.close() + _env_get(db, self._tracking_open, tracking_email_id, None, **kw) # Always return GIF blank image response = werkzeug.wrappers.Response() diff --git a/mail_tracking/data/tracking_data.xml b/mail_tracking/data/tracking_data.xml index 4bcb2cb8de..320ca782f6 100644 --- a/mail_tracking/data/tracking_data.xml +++ b/mail_tracking/data/tracking_data.xml @@ -1,13 +1,9 @@ - - - - - MailTracking Timestamp - 6 - - - - + + + MailTracking Timestamp + 6 + + diff --git a/mail_tracking/models/ir_mail_server.py b/mail_tracking/models/ir_mail_server.py index e364a57081..2908ee7745 100644 --- a/mail_tracking/models/ir_mail_server.py +++ b/mail_tracking/models/ir_mail_server.py @@ -4,7 +4,7 @@ import re import threading -from openerp import models, api, tools +from odoo import models, api, tools class IrMailServer(models.Model): diff --git a/mail_tracking/models/mail_mail.py b/mail_tracking/models/mail_mail.py index b33a1c5e30..58df138503 100644 --- a/mail_tracking/models/mail_mail.py +++ b/mail_tracking/models/mail_mail.py @@ -6,7 +6,7 @@ from datetime import datetime from email.utils import COMMASPACE -from openerp import models, api, fields +from odoo import models, fields class MailMail(models.Model): @@ -18,7 +18,7 @@ def _tracking_email_prepare(self, partner, email): email_to_list = email.get('email_to', []) email_to = COMMASPACE.join(email_to_list) return { - 'name': email.get('subject', False), + 'name': self.subject, 'timestamp': '%.6f' % ts, 'time': fields.Datetime.to_string(dt), 'mail_id': self.id, @@ -28,7 +28,6 @@ def _tracking_email_prepare(self, partner, email): 'sender': self.email_from, } - @api.multi def send_get_email_dict(self, partner=None): email = super(MailMail, self).send_get_email_dict(partner=partner) vals = self._tracking_email_prepare(partner, email) diff --git a/mail_tracking/models/mail_message.py b/mail_tracking/models/mail_message.py index fc6562bffc..5bd499e071 100644 --- a/mail_tracking/models/mail_message.py +++ b/mail_tracking/models/mail_message.py @@ -2,7 +2,7 @@ # © 2016 Antonio Espinosa - # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from openerp import models, api +from odoo import models, api class MailMessage(models.Model): @@ -31,7 +31,6 @@ def _partner_tracking_status_get(self, tracking_email): status = tracking_status_map.get(tracking_email_status, 'unknown') return status - @api.multi def tracking_status(self): res = {} for message in self: @@ -45,7 +44,7 @@ def tracking_status(self): for tracking in trackings: status = self._partner_tracking_status_get(tracking) recipient = ( - tracking.partner_id.display_name or tracking.recipient) + tracking.partner_id.name or tracking.recipient) partner_trackings.append(( status, tracking.id, recipient, tracking.partner_id.id)) if tracking.partner_id: @@ -60,7 +59,7 @@ def tracking_status(self): for partner in partners: # If there is partners not included, then status is 'unknown' partner_trackings.append(( - 'unknown', False, partner.display_name, partner.id)) + 'unknown', False, partner.name, partner.id)) res[message.id] = partner_trackings return res diff --git a/mail_tracking/models/mail_tracking_email.py b/mail_tracking/models/mail_tracking_email.py index 7e2202bd5e..32b5d7d9fb 100644 --- a/mail_tracking/models/mail_tracking_email.py +++ b/mail_tracking/models/mail_tracking_email.py @@ -8,8 +8,8 @@ import re from datetime import datetime -from openerp import models, api, fields, tools -import openerp.addons.decimal_precision as dp +from odoo import models, api, fields, tools +import odoo.addons.decimal_precision as dp _logger = logging.getLogger(__name__) @@ -23,14 +23,19 @@ class MailTrackingEmail(models.Model): _rec_name = 'display_name' _description = 'MailTracking email' + # This table is going to grow fast and to infinite, so we index: + # - name: Search in tree view + # - time: default order fields + # - recipient_address: Used for email_store calculation (non-store) + # - state: Search and group_by in tree view name = fields.Char(string="Subject", readonly=True, index=True) display_name = fields.Char( string="Display name", readonly=True, store=True, - compute="_compute_display_name") + compute="_compute_tracking_display_name") timestamp = fields.Float( string='UTC timestamp', readonly=True, digits=dp.get_precision('MailTracking Timestamp')) - time = fields.Datetime(string="Time", readonly=True) + time = fields.Datetime(string="Time", readonly=True, index=True) date = fields.Date( string="Date", readonly=True, compute="_compute_date", store=True) mail_message_id = fields.Many2one( @@ -42,7 +47,7 @@ class MailTrackingEmail(models.Model): recipient = fields.Char(string='Recipient email', readonly=True) recipient_address = fields.Char( string='Recipient email address', readonly=True, store=True, - compute='_compute_recipient_address') + compute='_compute_recipient_address', index=True) sender = fields.Char(string='Sender email', readonly=True) state = fields.Selection([ ('error', 'Error'), @@ -88,72 +93,56 @@ class MailTrackingEmail(models.Model): inverse_name='tracking_email_id', readonly=True) @api.model - def tracking_ids_recalculate(self, model, email_field, tracking_field, - email, new_tracking=None): - objects = self.env[model].search([ - (email_field, '=ilike', email), - ]) - for obj in objects: - trackings = obj[tracking_field] - if new_tracking: - trackings |= new_tracking - trackings = trackings._email_score_tracking_filter() - if set(obj[tracking_field].ids) != set(trackings.ids): - if trackings: - obj.write({ - tracking_field: [(6, False, trackings.ids)] - }) - else: - obj.write({ - tracking_field: [(5, False, False)] - }) - return objects + def _email_score_tracking_filter(self, domain, order='time desc', + limit=10): + """Default tracking search. Ready to be inherited.""" + return self.search(domain, limit=limit, order=order) @api.model - def _tracking_ids_to_write(self, email): - trackings = self.env['mail.tracking.email'].search([ - ('recipient_address', '=ilike', email) - ]) - trackings = trackings._email_score_tracking_filter() - if trackings: - return [(6, False, trackings.ids)] - else: - return [(5, False, False)] - - @api.multi - def _email_score_tracking_filter(self): - """Default email score filter for tracking emails""" - # Consider only last 10 tracking emails - return self.sorted(key=lambda r: r.time, reverse=True)[:10] + def email_is_bounced(self, email): + return len(self._email_score_tracking_filter([ + ('recipient_address', '=ilike', email), + ('state', 'in', ('error', 'rejected', 'spam', 'bounced')), + ])) > 0 @api.model def email_score_from_email(self, email): - trackings = self.env['mail.tracking.email'].search([ + return self._email_score_tracking_filter([ ('recipient_address', '=ilike', email) - ]) - return trackings.email_score() + ]).email_score() + + @api.model + def _email_score_weights(self): + """Default email score weights. Ready to be inherited""" + return { + 'error': -50.0, + 'rejected': -25.0, + 'spam': -25.0, + 'bounced': -25.0, + 'soft-bounced': -10.0, + 'unsub': -10.0, + 'delivered': 1.0, + 'opened': 5.0, + } - @api.multi def email_score(self): - """Default email score algorimth""" + """Default email score algorimth. Ready to be inherited + + Must return a value beetwen 0.0 and 100.0 + - Bad reputation: Value between 0 and 50.0 + - Unknown reputation: Value 50.0 + - Good reputation: Value between 50.0 and 100.0 + """ + weights = self._email_score_weights() score = 50.0 - trackings = self._email_score_tracking_filter() - for tracking in trackings: - if tracking.state in ('error',): - score -= 50.0 - elif tracking.state in ('rejected', 'spam', 'bounced'): - score -= 25.0 - elif tracking.state in ('soft-bounced', 'unsub'): - score -= 10.0 - elif tracking.state in ('delivered',): - score += 5.0 - elif tracking.state in ('opened',): - score += 10.0 + for tracking in self: + score += weights.get(tracking.state, 0.0) if score > 100.0: score = 100.0 + elif score < 0.0: + score = 0.0 return score - @api.multi @api.depends('recipient') def _compute_recipient_address(self): for email in self: @@ -163,32 +152,24 @@ def _compute_recipient_address(self): else: email.recipient_address = email.recipient - @api.multi @api.depends('name', 'recipient') - def _compute_display_name(self): + def _compute_tracking_display_name(self): for email in self: parts = [email.name or ''] if email.recipient: parts.append(email.recipient) email.display_name = ' - '.join(parts) - @api.multi @api.depends('time') def _compute_date(self): for email in self: email.date = fields.Date.to_string( fields.Date.from_string(email.time)) - @api.model - def create(self, vals): - tracking = super(MailTrackingEmail, self).create(vals) - self.tracking_ids_recalculate( - 'res.partner', 'email', 'tracking_email_ids', - tracking.recipient_address, new_tracking=tracking) - return tracking - def _get_mail_tracking_img(self): - base_url = self.env['ir.config_parameter'].get_param('web.base.url') + m_config = self.env['ir.config_parameter'] + base_url = (m_config.get_param('mail_tracking.base.url') or + m_config.get_param('web.base.url')) path_url = ( 'mail/tracking/open/%(db)s/%(tracking_email_id)s/blank.gif' % { 'db': self.env.cr.dbname, @@ -202,7 +183,12 @@ def _get_mail_tracking_img(self): 'tracking_email_id': self.id, }) - @api.multi + def _partners_email_bounced_set(self, reason): + for tracking_email in self: + self.env['res.partner'].search([ + ('email', '=ilike', tracking_email.recipient_address) + ]).email_bounced_set(tracking_email, reason) + def smtp_error(self, mail_server, smtp_server, exception): self.sudo().write({ 'error_smtp_server': tools.ustr(smtp_server), @@ -210,9 +196,9 @@ def smtp_error(self, mail_server, smtp_server, exception): 'error_description': tools.ustr(exception), 'state': 'error', }) + self.sudo()._partners_email_bounced_set('error') return True - @api.multi def tracking_img_add(self, email): self.ensure_one() tracking_url = self._get_mail_tracking_img() @@ -240,7 +226,6 @@ def _message_partners_check(self, message, message_id): }) return True - @api.multi def _tracking_sent_prepare(self, mail_server, smtp_server, message, message_id): self.ensure_one() @@ -286,7 +271,6 @@ def _concurrent_events(self, event_type, metadata): concurrent_event_ids = m_event.search(domain) return concurrent_event_ids - @api.multi def event_create(self, event_type, metadata): event_ids = self.env['mail.tracking.event'] for tracking_email in self: @@ -295,13 +279,10 @@ def event_create(self, event_type, metadata): vals = tracking_email._event_prepare(event_type, metadata) if vals: event_ids += event_ids.sudo().create(vals) - partners = self.tracking_ids_recalculate( - 'res.partner', 'email', 'tracking_email_ids', - tracking_email.recipient_address) - if partners: - partners.email_score_calculate() else: _logger.debug("Concurrent event '%s' discarded", event_type) + if event_type in {'hard_bounce', 'spam', 'reject'}: + self.sudo()._partners_email_bounced_set(event_type) return event_ids @api.model diff --git a/mail_tracking/models/mail_tracking_event.py b/mail_tracking/models/mail_tracking_event.py index 728f88c040..3bd0fda7ff 100644 --- a/mail_tracking/models/mail_tracking_event.py +++ b/mail_tracking/models/mail_tracking_event.py @@ -5,8 +5,8 @@ import time from datetime import datetime -from openerp import models, api, fields -import openerp.addons.decimal_precision as dp +from odoo import models, api, fields +import odoo.addons.decimal_precision as dp class MailTrackingEvent(models.Model): @@ -23,7 +23,7 @@ class MailTrackingEvent(models.Model): date = fields.Date( string="Date", readonly=True, compute="_compute_date", store=True) tracking_email_id = fields.Many2one( - string='Message', readonly=True, + string='Message', readonly=True, required=True, ondelete='cascade', comodel_name='mail.tracking.email') event_type = fields.Selection(string='Event type', selection=[ ('sent', 'Sent'), @@ -51,7 +51,6 @@ class MailTrackingEvent(models.Model): error_description = fields.Char(string='Error description', readonly=True) error_details = fields.Text(string='Error details', readonly=True) - @api.multi @api.depends('time') def _compute_date(self): for email in self: diff --git a/mail_tracking/models/res_partner.py b/mail_tracking/models/res_partner.py index 908f11904b..8d36c7c4ee 100644 --- a/mail_tracking/models/res_partner.py +++ b/mail_tracking/models/res_partner.py @@ -2,45 +2,46 @@ # © 2016 Antonio Espinosa - # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from openerp import models, api, fields +from odoo import models, api, fields class ResPartner(models.Model): _inherit = 'res.partner' - tracking_email_ids = fields.Many2many( - string="Tracking emails", comodel_name="mail.tracking.email", - readonly=True) + # tracking_emails_count and email_score are non-store fields in order + # to improve performance + # email_bounced is store=True and index=True field in order to filter + # in tree view for processing bounces easier tracking_emails_count = fields.Integer( - string="Tracking emails count", store=True, readonly=True, - compute="_compute_tracking_emails_count") - email_score = fields.Float( - string="Email score", readonly=True, default=50.0) + compute='_compute_tracking_emails_count', readonly=True) + email_bounced = fields.Boolean(index=True) + email_score = fields.Float(compute='_compute_email_score', readonly=True) - @api.multi - def email_score_calculate(self): - # This is not a compute method because is causing a inter-block - # in mail_tracking_email PostgreSQL table - # We suspect that tracking_email write to state field block that - # table and then inside write ORM try to read from DB - # tracking_email_ids because it's not in cache. - # PostgreSQL blocks read because we have not committed yet the write - for partner in self: - partner.email_score = partner.tracking_email_ids.email_score() + @api.depends('email') + def _compute_email_score(self): + for partner in self.filtered('email'): + partner.email_score = self.env['mail.tracking.email'].\ + email_score_from_email(partner.email) - @api.one - @api.depends('tracking_email_ids') + @api.multi + @api.depends('email') def _compute_tracking_emails_count(self): - self.tracking_emails_count = self.env['mail.tracking.email'].\ - search_count([ - ('recipient_address', '=ilike', self.email) - ]) + for partner in self: + partner.tracking_emails_count = self.env['mail.tracking.email'].\ + search_count([ + ('recipient_address', '=ilike', partner.email) + ]) @api.multi + def email_bounced_set(self, tracking_email, reason): + """Inherit this method to make any other actions to partners""" + partners = self.filtered(lambda r: not r.email_bounced) + return partners.write({'email_bounced': True}) + def write(self, vals): email = vals.get('email') if email is not None: - m_track = self.env['mail.tracking.email'] - vals['tracking_email_ids'] = m_track._tracking_ids_to_write(email) - vals['email_score'] = m_track.email_score_from_email(email) + vals['email_bounced'] = ( + bool(email) and + self.env['mail.tracking.email'].email_is_bounced(email)) return super(ResPartner, self).write(vals) diff --git a/mail_tracking/security/ir.model.access.csv b/mail_tracking/security/ir.model.access.csv index ab17dc3348..2e66abeada 100644 --- a/mail_tracking/security/ir.model.access.csv +++ b/mail_tracking/security/ir.model.access.csv @@ -1,4 +1,6 @@ "id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"access_mail_tracking_email_group_public","mail_tracking_email group_public","model_mail_tracking_email","base.group_public",1,0,0,0 +"access_mail_tracking_email_group_portal","mail_tracking_email group_portal","model_mail_tracking_email","base.group_portal",1,0,0,0 "access_mail_tracking_email_group_user","mail_tracking_email group_user","model_mail_tracking_email","base.group_user",1,0,0,0 "access_mail_tracking_event_group_user","mail_tracking_event group_user","model_mail_tracking_event","base.group_user",1,0,0,0 "access_mail_tracking_email_group_system","mail_tracking_email group_system","model_mail_tracking_email","base.group_system",1,1,1,1 diff --git a/mail_tracking/security/mail_tracking_email_security.xml b/mail_tracking/security/mail_tracking_email_security.xml new file mode 100644 index 0000000000..3928ff7fcb --- /dev/null +++ b/mail_tracking/security/mail_tracking_email_security.xml @@ -0,0 +1,16 @@ + + + + + + mail_tracking_email: portal/public: read access on my email trackings + + [('partner_id', '=', user.partner_id.id)] + + + + + + + diff --git a/mail_tracking/static/src/js/mail_tracking.js b/mail_tracking/static/src/js/mail_tracking.js index 473b9f7aed..7e1daa5e19 100644 --- a/mail_tracking/static/src/js/mail_tracking.js +++ b/mail_tracking/static/src/js/mail_tracking.js @@ -4,7 +4,6 @@ odoo.define('mail_tracking.partner_tracking', function(require){ "use strict"; -var $ = require('$'); var core = require('web.core'); var session = require('web.session'); var Model = require('web.Model'); @@ -26,7 +25,7 @@ chat_manager.make_message = function(data) { ChatThread.include({ on_tracking_partner_click: function (event) { - var partner_id = $(event.currentTarget).data('partner'); + var partner_id = this.$el.find(event.currentTarget).data('partner'); var state = { 'model': 'res.partner', 'id': partner_id, diff --git a/mail_tracking/tests/test_mail_tracking.py b/mail_tracking/tests/test_mail_tracking.py index 5862db905e..4e5dea88e7 100644 --- a/mail_tracking/tests/test_mail_tracking.py +++ b/mail_tracking/tests/test_mail_tracking.py @@ -5,12 +5,12 @@ import mock import base64 import time -from openerp.tests.common import TransactionCase +from odoo import http +from odoo.tests.common import TransactionCase from ..controllers.main import MailTrackingController, BLANK -mock_request = 'openerp.http.request' -mock_send_email = ('openerp.addons.base.ir.ir_mail_server.' - 'ir_mail_server.send_email') +mock_send_email = ('odoo.addons.base.ir.ir_mail_server.' + 'IrMailServer.send_email') class FakeUserAgent(object): @@ -22,11 +22,9 @@ def __str__(self): return 'Test suite' -# One test case per method class TestMailTracking(TransactionCase): - # Use case : Prepare some data for current test case - def setUp(self): - super(TestMailTracking, self).setUp() + def setUp(self, *args, **kwargs): + super(TestMailTracking, self).setUp(*args, **kwargs) self.sender = self.env['res.partner'].create({ 'name': 'Test sender', 'email': 'sender@example.com', @@ -37,12 +35,22 @@ def setUp(self): 'email': 'recipient@example.com', 'notify_email': 'always', }) - self.request = { + self.last_request = http.request + http.request = type('obj', (object,), { + 'db': self.env.cr.dbname, + 'env': self.env, + 'endpoint': type('obj', (object,), { + 'routing': [], + }), 'httprequest': type('obj', (object,), { 'remote_addr': '123.123.123.123', 'user_agent': FakeUserAgent(), }), - } + }) + + def tearDown(self, *args, **kwargs): + http.request = self.last_request + return super(TestMailTracking, self).tearDown(*args, **kwargs) def test_message_post(self): # This message will generate a notification for recipient @@ -50,7 +58,7 @@ def test_message_post(self): 'subject': 'Message test', 'author_id': self.sender.id, 'email_from': self.sender.email, - 'type': 'comment', + 'message_type': 'comment', 'model': 'res.partner', 'res_id': self.recipient.id, 'partner_ids': [(4, self.recipient.id)], @@ -65,9 +73,7 @@ def test_message_post(self): self.assertTrue(tracking_email) self.assertEqual(tracking_email.state, 'sent') # message_dict read by web interface - message_dict = message.message_read() - # First item in threads is message content - message_dict = message_dict['threads'][0][0] + message_dict = message.message_format()[0] self.assertTrue(len(message_dict['partner_ids']) > 0) # First partner is recipient partner_id = message_dict['partner_ids'][0][0] @@ -110,10 +116,15 @@ def test_mail_send(self): mail, tracking = self.mail_send(self.recipient.email) self.assertEqual(mail.email_to, tracking.recipient) self.assertEqual(mail.email_from, tracking.sender) - with mock.patch(mock_request) as mock_func: - mock_func.return_value = type('obj', (object,), self.request) - res = controller.mail_tracking_open(db, tracking.id) - self.assertEqual(image, res.response[0]) + res = controller.mail_tracking_open(db, tracking.id) + self.assertEqual(image, res.response[0]) + # Two events: sent and open + self.assertEqual(2, len(tracking.tracking_event_ids)) + # Fake event: tracking_email_id = False + res = controller.mail_tracking_open(db, False) + self.assertEqual(image, res.response[0]) + # Two events again because no tracking_email_id found for False + self.assertEqual(2, len(tracking.tracking_event_ids)) def test_concurrent_open(self): mail, tracking = self.mail_send(self.recipient.email) @@ -193,31 +204,38 @@ def test_smtp_error(self): self.assertEqual('error', tracking.state) self.assertEqual('Warning', tracking.error_type) self.assertEqual('Test error', tracking.error_description) + self.assertTrue(self.recipient.email_bounced) def test_partner_email_change(self): mail, tracking = self.mail_send(self.recipient.email) tracking.event_create('open', {}) orig_score = self.recipient.email_score + orig_count = self.recipient.tracking_emails_count orig_email = self.recipient.email self.recipient.email = orig_email + '2' self.assertEqual(50.0, self.recipient.email_score) + self.assertEqual(0, self.recipient.tracking_emails_count) self.recipient.email = orig_email self.assertEqual(orig_score, self.recipient.email_score) + self.assertEqual(orig_count, self.recipient.tracking_emails_count) def test_process_hard_bounce(self): mail, tracking = self.mail_send(self.recipient.email) tracking.event_create('hard_bounce', {}) self.assertEqual('bounced', tracking.state) + self.assertTrue(self.recipient.email_score < 50.0) def test_process_soft_bounce(self): mail, tracking = self.mail_send(self.recipient.email) tracking.event_create('soft_bounce', {}) self.assertEqual('soft-bounced', tracking.state) + self.assertTrue(self.recipient.email_score < 50.0) def test_process_delivered(self): mail, tracking = self.mail_send(self.recipient.email) tracking.event_create('delivered', {}) self.assertEqual('delivered', tracking.state) + self.assertTrue(self.recipient.email_score > 50.0) def test_process_deferral(self): mail, tracking = self.mail_send(self.recipient.email) @@ -228,35 +246,45 @@ def test_process_spam(self): mail, tracking = self.mail_send(self.recipient.email) tracking.event_create('spam', {}) self.assertEqual('spam', tracking.state) + self.assertTrue(self.recipient.email_score < 50.0) def test_process_unsub(self): mail, tracking = self.mail_send(self.recipient.email) tracking.event_create('unsub', {}) self.assertEqual('unsub', tracking.state) + self.assertTrue(self.recipient.email_score < 50.0) def test_process_reject(self): mail, tracking = self.mail_send(self.recipient.email) tracking.event_create('reject', {}) self.assertEqual('rejected', tracking.state) + self.assertTrue(self.recipient.email_score < 50.0) def test_process_open(self): mail, tracking = self.mail_send(self.recipient.email) tracking.event_create('open', {}) self.assertEqual('opened', tracking.state) + self.assertTrue(self.recipient.email_score > 50.0) def test_process_click(self): mail, tracking = self.mail_send(self.recipient.email) tracking.event_create('click', {}) self.assertEqual('opened', tracking.state) + self.assertTrue(self.recipient.email_score > 50.0) + + def test_process_several_bounce(self): + for i in range(1, 10): + mail, tracking = self.mail_send(self.recipient.email) + tracking.event_create('hard_bounce', {}) + self.assertEqual('bounced', tracking.state) + self.assertEqual(0.0, self.recipient.email_score) def test_db(self): db = self.env.cr.dbname controller = MailTrackingController() - with mock.patch(mock_request) as mock_func: - mock_func.return_value = type('obj', (object,), self.request) - not_found = controller.mail_tracking_all('not_found_db') - self.assertEqual('NOT FOUND', not_found.response[0]) - none = controller.mail_tracking_all(db) - self.assertEqual('NONE', none.response[0]) - none = controller.mail_tracking_event(db, 'open') - self.assertEqual('NONE', none.response[0]) + not_found = controller.mail_tracking_all('not_found_db') + self.assertEqual('NOT FOUND', not_found.response[0]) + none = controller.mail_tracking_all(db) + self.assertEqual('NONE', none.response[0]) + none = controller.mail_tracking_event(db, 'open') + self.assertEqual('NONE', none.response[0]) diff --git a/mail_tracking/views/assets.xml b/mail_tracking/views/assets.xml index e4cac0d78f..685721a9a4 100644 --- a/mail_tracking/views/assets.xml +++ b/mail_tracking/views/assets.xml @@ -2,16 +2,14 @@ - -