<commit_before># -*- coding: utf-8 -*-
# Copyright 2016 <NAME> - <<EMAIL>>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import hashlib
import hmac
from datetime import datetime
from openerp import models, api, fields

import logging
_logger = logging.getLogger(__name__)


class MailTrackingEmail(models.Model):
    _inherit = "mail.tracking.email"

    def _country_search(self, country_code):
        country = False
        if country_code:
            country = self.env['res.country'].search([
                ('code', '=', country_code.upper()),
            ])
        if country:
            return country.id
        return False

    @property
    def _mailgun_mandatory_fields(self):
        return ('event', 'timestamp', 'token', 'signature',
                'tracking_email_id', 'odoo_db')

    @property
    def _mailgun_event_type_mapping(self):
        return {
            # Mailgun event type: tracking event type
            'delivered': 'delivered',
            'opened': 'open',
            'clicked': 'click',
            'unsubscribed': 'unsub',
            'complained': 'spam',
            'bounced': 'hard_bounce',
            'dropped': 'reject',
        }

    @property
    def _mailgun_supported_event_types(self):
        return self._mailgun_event_type_mapping.keys()

    def _mailgun_event_type_verify(self, event):
        event = event or {}
        mailgun_event_type = event.get('event')
        if mailgun_event_type not in self._mailgun_supported_event_types:
            _logger.info("Mailgun: event type '%s' not supported",
                         mailgun_event_type)
            return False
        # OK, event type is valid
        return True

    def _mailgun_signature(self, api_key, timestamp, token):
        return hmac.new(
            key=str(api_key),
            msg='{}{}'.format(str(timestamp), str(token)),
            digestmod=hashlib.sha256).hexdigest()

    def _mailgun_signature_verify(self, event):
        event = event or {}
        api_key = self.env['ir.config_parameter'].get_param('mailgun.apikey')
        if not api_key:
            _logger.info("No Mailgun api key configured. "
                         "Please add 'mailgun.apikey' to System parameters "
                         "to enable Mailgun authentication webhoook requests. "
                         "More info at: "
                         "https://documentation.mailgun.com/user_manual.html"
                         "#webhooks")
        else:
            timestamp = event.get('timestamp')
            token = event.get('token')
            signature = event.get('signature')
            event_digest = self._mailgun_signature(api_key, timestamp, token)
            if signature != event_digest:
                _logger.error("Mailgun: Invalid signature '%s' != '%s'",
                              signature, event_digest)
                return False
        # OK, signature is valid
        return True

    def _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.info("Mailgun: Database '%s' is not the current database",
                         odoo_db)
            return False
        # OK, DB is current
        return True

    def _mailgun_metadata(self, mailgun_event_type, event, metadata):
        # Get Mailgun timestamp when found
        ts = event.get('timestamp', False)
        try:
            ts = float(ts)
        except:
            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
        mapping = {
            'recipient': 'recipient',
            'ip': 'ip',
            'user_agent': 'user-agent',
            'os_family': 'client-os',
            'ua_family': 'client-name',
            'ua_type': 'client-type',
            'url': 'url',
        }
        for k, v in mapping.iteritems():
            if event.get(v, False):
                metadata[k] = event[v]
        # Special field mapping
        metadata.update({
            'mobile': event.get('device-type') in ('mobile', 'tablet'),
            'user_country_id': self._country_search(
                event.get('country', False)),
        })
        # Mapping for special events
        if mailgun_event_type == 'bounced':
            metadata.update({
                'error_type': event.get('code', False),
                'error_description': event.get('error', False),
                'error_details': event.get('notification', False),
            })
        elif mailgun_event_type == 'dropped':
            metadata.update({
                'error_type': event.get('reason', False),
                'error_description': event.get('code', False),
                'error_details': event.get('description', False),
            })
        elif mailgun_event_type == 'complained':
            metadata.update({
                'error_type': 'spam',
                'error_description':<commit_msg>[8.0][IMP][mail_tracking] Speed installation time and discard concurrent events (#82)<commit_after># -*- coding: utf-8 -*-
# Copyright 2016 <NAME> - <<EMAIL>>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import hashlib
import hmac
from datetime import datetime
from openerp import models, api, fields

import logging
_logger = logging.getLogger(__name__)


class MailTrackingEmail(models.Model):
    _inherit = "mail.tracking.email"

    def _country_search(self, country_code):
        country = False
        if country_code:
            country = self.env['res.country'].search([
                ('code', '=', country_code.upper()),
            ])
        if country:
            return country.id
        return False

    @property
    def _mailgun_mandatory_fields(self):
        return ('event', 'timestamp', 'token', 'signature',
                'tracking_email_id', 'odoo_db')

    @property
    def _mailgun_event_type_mapping(self):
        return {
            # Mailgun event type: tracking event type
            'delivered': 'delivered',
            'opened': 'open',
            'clicked': 'click',
            'unsubscribed': 'unsub',
            'complained': 'spam',
            'bounced': 'hard_bounce',
            'dropped': 'reject',
        }

    def _mailgun_event_type_verify(self, event):
        event = event or {}
        mailgun_event_type = event.get('event')
        if mailgun_event_type not in self._mailgun_event_type_mapping:
            _logger.error("Mailgun: event type '%s' not supported",
                          mailgun_event_type)
            return False
        # OK, event type is valid
        return True

    def _mailgun_signature(self, api_key, timestamp, token):
        return hmac.new(
            key=str(api_key),
            msg='{}{}'.format(str(timestamp), str(token)),
            digestmod=hashlib.sha256).hexdigest()

    def _mailgun_signature_verify(self, event):
        event = event or {}
        api_key = self.env['ir.config_parameter'].get_param('mailgun.apikey')
        if not api_key:
            _logger.warning("No Mailgun api key configured. "
                            "Please add 'mailgun.apikey' to System parameters "
                            "to enable Mailgun authentication webhoook "
                            "requests. More info at: "
                            "https://documentation.mailgun.com/"
                            "user_manual.html#webhooks")
        else:
            timestamp = event.get('timestamp')
            token = event.get('token')
            signature = event.get('signature')
            event_digest = self._mailgun_signature(api_key, timestamp, token)
            if signature != event_digest:
                _logger.error("Mailgun: Invalid signature '%s' != '%s'",
                              signature, event_digest)
                return False
        # OK, signature is valid
        return True

    def _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("Mailgun: Database '%s' is not the current database",
                          odoo_db)
            return False
        # OK, DB is current
        return True

    def _mailgun_metadata(self, mailgun_event_type, event, metadata):
        # Get Mailgun timestamp when found
        ts = event.get('timestamp', False)
        try:
            ts = float(ts)
        except:
            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
        mapping = {
            'recipient': 'recipient',
            'ip': 'ip',
            'user_agent': 'user-agent',
            'os_family': 'client-os',
            'ua_family': 'client-name',
            'ua_type': 'client-type',
            'url': 'url',
        }
        for k, v in mapping.iteritems():
            if event.get(v, False):
                metadata[k] = event[v]
        # Special field mapping
        metadata.update({
            'mobile': event.get('device-type') in {'mobile', 'tablet'},
            'user_country_id': self._country_search(
                event.get('country', False)),
        })
        # Mapping for special events
        if mailgun_event_type == 'bounced':
            metadata.update({
                'error_type': event.get('code', False),
                'error_description': event.get('error', False),
                'error_details': event.get('notification', False),
            })
        elif mailgun_event_type == 'dropped':
            metadata.update({
                'error_type': event.get('reason', False),
                'error_description': event.get('code', False),
                'error_details': event.get('description', False),
            })
        elif mailgun_event_type == 'complained':
            metadata.update({
                'error_type': 'spam',
                'error_description':
{'fraction_non_alphanumeric': 0.11527764457658003, 'fraction_numerical': 0.0023017166970365397, 'mean_word_length': 1.777097203728362, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4886792', 'n_tokens_mistral': 2886, 'n_tokens_neox': 2779, 'n_words': 662}