Browse Source

Merge pull request #108 from dbo-odoo/10.0-mig-mail_tracking

[MIG] mail_tracking: Migrated to 10.0
pull/147/head
Pedro M. Baeza 8 years ago
committed by GitHub
parent
commit
d42d5a96f6
  1. 2
      mail_tracking/README.rst
  2. 5
      mail_tracking/__manifest__.py
  3. 71
      mail_tracking/controllers/main.py
  4. 12
      mail_tracking/data/tracking_data.xml
  5. 2
      mail_tracking/models/ir_mail_server.py
  6. 5
      mail_tracking/models/mail_mail.py
  7. 7
      mail_tracking/models/mail_message.py
  8. 139
      mail_tracking/models/mail_tracking_email.py
  9. 7
      mail_tracking/models/mail_tracking_event.py
  10. 51
      mail_tracking/models/res_partner.py
  11. 2
      mail_tracking/security/ir.model.access.csv
  12. 16
      mail_tracking/security/mail_tracking_email_security.xml
  13. 3
      mail_tracking/static/src/js/mail_tracking.js
  14. 64
      mail_tracking/tests/test_mail_tracking.py
  15. 6
      mail_tracking/views/assets.xml
  16. 15
      mail_tracking/views/res_partner_view.xml

2
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

5
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",

71
mail_tracking/controllers/main.py

@ -4,15 +4,20 @@
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
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:
@ -20,8 +25,16 @@ def _env_get(db):
except: # pragma: no cover
_logger.warning("Selected BD '%s' connection error", db)
if reg:
return api.Environment(reg.cursor(), SUPERUSER_ID, {})
return False
_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 @@ class MailTrackingController(http.Controller):
'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/<string:db>',
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/<string:db>/<string:event_type>',
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/<string:db>'
'/<int:tracking_email_id>/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()

12
mail_tracking/data/tracking_data.xml

@ -1,13 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- © 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<openerp>
<data>
<record forcecreate="True" id="decimal_tracking_timestamp" model="decimal.precision">
<odoo>
<record forcecreate="True" id="decimal_tracking_timestamp" model="decimal.precision">
<field name="name">MailTracking Timestamp</field>
<field name="digits">6</field>
</record>
</data>
</openerp>
</record>
</odoo>

2
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):

5
mail_tracking/models/mail_mail.py

@ -6,7 +6,7 @@ import time
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 @@ class MailMail(models.Model):
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 @@ class MailMail(models.Model):
'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)

7
mail_tracking/models/mail_message.py

@ -2,7 +2,7 @@
# © 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
# 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 @@ class MailMessage(models.Model):
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 @@ class MailMessage(models.Model):
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 @@ class MailMessage(models.Model):
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

139
mail_tracking/models/mail_tracking_email.py

@ -8,8 +8,8 @@ import time
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 @@ class MailTrackingEmail(models.Model):
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 @@ class MailTrackingEmail(models.Model):
'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 @@ class MailTrackingEmail(models.Model):
'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 @@ class MailTrackingEmail(models.Model):
})
return True
@api.multi
def _tracking_sent_prepare(self, mail_server, smtp_server, message,
message_id):
self.ensure_one()
@ -286,7 +271,6 @@ class MailTrackingEmail(models.Model):
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 @@ class MailTrackingEmail(models.Model):
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

7
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:

51
mail_tracking/models/res_partner.py

@ -2,45 +2,46 @@
# © 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
# 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'].\
for partner in self:
partner.tracking_emails_count = self.env['mail.tracking.email'].\
search_count([
('recipient_address', '=ilike', self.email)
('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)

2
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

16
mail_tracking/security/mail_tracking_email_security.xml

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- © 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<odoo>
<record model="ir.rule" id="mail_tracking_email_portal_public_rule">
<field name="name">mail_tracking_email: portal/public: read access on my email trackings</field>
<field name="model_id" ref="model_mail_tracking_email"/>
<field name="domain_force">[('partner_id', '=', user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('base.group_portal')), (4, ref('base.group_public'))]"/>
<field name="perm_create" eval="False"/>
<field name="perm_unlink" eval="False"/>
<field name="perm_write" eval="False"/>
</record>
</odoo>

3
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,

64
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 @@ class FakeUserAgent(object):
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 @@ class TestMailTracking(TransactionCase):
'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 @@ class TestMailTracking(TransactionCase):
'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 @@ class TestMailTracking(TransactionCase):
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 @@ class TestMailTracking(TransactionCase):
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])
# 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 @@ class TestMailTracking(TransactionCase):
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,32 +246,42 @@ class TestMailTracking(TransactionCase):
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)

6
mail_tracking/views/assets.xml

@ -2,8 +2,7 @@
<!-- © 2016 Antonio Espinosa - <antonio.espinosa@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -->
<odoo>
<template id="assets_backend"
<template id="assets_backend"
name="mail_tracking assets"
inherit_id="web.assets_backend">
<xpath expr="." position="inside">
@ -12,6 +11,5 @@
<script type="text/javascript"
src="/mail_tracking/static/src/js/mail_tracking.js"/>
</xpath>
</template>
</template>
</odoo>

15
mail_tracking/views/res_partner_view.xml

@ -24,7 +24,22 @@
<field name="email" position="after">
<field name="email_score" widget="progressbar"
attrs="{'invisible': [('email', '=', False)]}"/>
<field name="email_bounced"
attrs="{'invisible': [('email', '=', False)]}"/>
</field>
</field>
</record>
<record model="ir.ui.view" id="view_res_partner_filter">
<field name="name">Filter bounced partners</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_res_partner_filter"/>
<field name="arch" type="xml">
<filter name="type_company" position="after">
<separator/>
<filter string="Email bounced" name="email_bounced"
domain="[('email', '!=' , False), ('email_bounced', '=', True)]"/>
</filter>
</field>
</record>

Loading…
Cancel
Save