You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
171 lines
6.4 KiB
171 lines
6.4 KiB
# -*- coding: utf-8 -*-
|
|
# Copyright 2016-2017 Compassion CH (http://www.compassion.ch)
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
import logging
|
|
from datetime import datetime
|
|
|
|
from werkzeug.useragents import UserAgent
|
|
|
|
from odoo import models, fields, api
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
class MailTrackingEmail(models.Model):
|
|
""" Count the user clicks on links inside e-mails sent.
|
|
Add tracking methods to process Sendgrid Notifications
|
|
"""
|
|
_inherit = 'mail.tracking.email'
|
|
|
|
click_count = fields.Integer(
|
|
compute='_compute_clicks', store=True, readonly=True)
|
|
|
|
@api.depends('tracking_event_ids')
|
|
def _compute_clicks(self):
|
|
for mail in self:
|
|
mail.click_count = self.env['mail.tracking.event'].search_count([
|
|
('event_type', '=', 'click'),
|
|
('tracking_email_id', '=', mail.id)
|
|
])
|
|
|
|
@property
|
|
def _sendgrid_mandatory_fields(self):
|
|
return ('event', 'timestamp', 'odoo_id', 'odoo_db')
|
|
|
|
@property
|
|
def _sendgrid_event_type_mapping(self):
|
|
return {
|
|
# Sendgrid event type: tracking event type
|
|
'bounce': 'hard_bounce',
|
|
'click': 'click',
|
|
'deferred': 'deferral',
|
|
'delivered': 'delivered',
|
|
'dropped': 'reject',
|
|
'group_unsubscribe': 'unsub',
|
|
'open': 'open',
|
|
'processed': 'sent',
|
|
'spamreport': 'spam',
|
|
'unsubscribe': 'unsub',
|
|
}
|
|
|
|
def _sendgrid_event_type_verify(self, event):
|
|
event = event or {}
|
|
sendgrid_event_type = event.get('event')
|
|
if sendgrid_event_type not in self._sendgrid_event_type_mapping:
|
|
_logger.error("Sendgrid: event type '%s' not supported",
|
|
sendgrid_event_type)
|
|
return False
|
|
# OK, event type is valid
|
|
return True
|
|
|
|
def _sendgrid_db_verify(self, event):
|
|
event = event or {}
|
|
odoo_db = event.get('odoo_db')
|
|
current_db = self.env.cr.dbname
|
|
if odoo_db != current_db:
|
|
_logger.error("Sendgrid: Database '%s' is not the current "
|
|
"database",
|
|
odoo_db)
|
|
return False
|
|
# OK, DB is current
|
|
return True
|
|
|
|
def _sendgrid_metadata(self, sendgrid_event_type, event, metadata):
|
|
# Get sendgrid timestamp when found
|
|
ts = event.get('timestamp')
|
|
try:
|
|
ts = float(ts)
|
|
except ValueError:
|
|
ts = False
|
|
if ts:
|
|
dt = datetime.utcfromtimestamp(ts)
|
|
metadata.update({
|
|
'timestamp': ts,
|
|
'time': fields.Datetime.to_string(dt),
|
|
'date': fields.Date.to_string(dt),
|
|
})
|
|
# Common field mapping (sendgrid_field: odoo_field)
|
|
mapping = {
|
|
'email': 'recipient',
|
|
'ip': 'ip',
|
|
'url': 'url',
|
|
}
|
|
for k, v in mapping.iteritems():
|
|
if event.get(k, False):
|
|
metadata[v] = event[k]
|
|
# Special field mapping
|
|
if event.get('useragent'):
|
|
user_agent = UserAgent(event['useragent'])
|
|
metadata.update({
|
|
'user_agent': user_agent.string,
|
|
'os_family': user_agent.platform,
|
|
'ua_family': user_agent.browser,
|
|
'mobile': user_agent.platform in [
|
|
'android', 'iphone', 'ipad']
|
|
})
|
|
# Mapping for special events
|
|
if sendgrid_event_type == 'bounce':
|
|
metadata.update({
|
|
'error_type': event.get('type', False),
|
|
'bounce_type': event.get('type', False),
|
|
'error_description': event.get('reason', False),
|
|
'bounce_description': event.get('reason', False),
|
|
'error_details': event.get('status', False),
|
|
})
|
|
elif sendgrid_event_type == 'dropped':
|
|
metadata.update({
|
|
'error_type': event.get('reason', False),
|
|
})
|
|
return metadata
|
|
|
|
def _sendgrid_tracking_get(self, event):
|
|
tracking = False
|
|
message_id = event.get('odoo_id', False)
|
|
if message_id:
|
|
tracking = self.search([
|
|
('mail_id.message_id', '=', message_id),
|
|
('recipient', '=ilike', event.get('email'))], limit=1)
|
|
return tracking
|
|
|
|
def _event_is_from_sendgrid(self, event):
|
|
event = event or {}
|
|
return all([k in event for k in self._sendgrid_mandatory_fields])
|
|
|
|
@api.model
|
|
def event_process(self, request, post, metadata, event_type=None):
|
|
res = super(MailTrackingEmail, self).event_process(
|
|
request, post, metadata, event_type=event_type)
|
|
is_json = hasattr(request, 'jsonrequest') and isinstance(
|
|
request.jsonrequest, list)
|
|
if res == 'NONE' and is_json:
|
|
for event in request.jsonrequest:
|
|
if self._event_is_from_sendgrid(event):
|
|
if not self._sendgrid_event_type_verify(event):
|
|
res = 'ERROR: Event type not supported'
|
|
elif not self._sendgrid_db_verify(event):
|
|
res = 'ERROR: Invalid DB'
|
|
else:
|
|
res = 'OK'
|
|
if res == 'OK':
|
|
sendgrid_event_type = event.get('event')
|
|
mapped_event_type = self._sendgrid_event_type_mapping.get(
|
|
sendgrid_event_type) or event_type
|
|
if not mapped_event_type:
|
|
res = 'ERROR: Bad event'
|
|
tracking = self._sendgrid_tracking_get(event)
|
|
if tracking:
|
|
# Complete metadata with sendgrid event info
|
|
metadata = self._sendgrid_metadata(
|
|
sendgrid_event_type, event, metadata)
|
|
# Create event
|
|
tracking.event_create(mapped_event_type, metadata)
|
|
else:
|
|
res = 'ERROR: Tracking not found'
|
|
if res != 'NONE':
|
|
if event_type:
|
|
_logger.info(
|
|
"sendgrid: event '%s' process '%s'",
|
|
event_type, res)
|
|
else:
|
|
_logger.info("sendgrid: event process '%s'", res)
|
|
return res
|