from datetime import datetime, timedelta from odoo.tools.safe_eval import safe_eval from odoo import api, fields, models, tools, _ from odoo.exceptions import Warning, ValidationError from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT from email.utils import formataddr, parseaddr REPLACEMENT_OF_KEY = [('id', 'mailchimp_id'), ('create_time', 'create_date'), ('send_time', 'sent_date'), ('type', 'mailchimp_champ_type')] DATE_CONVERSION = ['create_date', 'sent_date'] UNWANTED_DATA = ['_links', 'created_by', 'edited_by', 'thumbnail'] class MassMailing(models.Model): _inherit = "mail.mass_mailing" create_date = fields.Datetime("Created on", readonly=True, index=True) mailchimp_template_id = fields.Many2one('mailchimp.templates', "MailChimp Template", copy=False) mailchimp_account_id = fields.Many2one('mailchimp.accounts', string="MailChimp Account", related="mailchimp_template_id.account_id", store=True) mailchimp_list_id = fields.Many2one("mailchimp.lists", string="MailChimp List") mailchimp_id = fields.Char("MailChimp ID", copy=False) mailchimp_segment_id = fields.Many2one('mailchimp.segments', string="MailChimp Segments", copy=False) mailchimp_champ_type = fields.Selection( [('regular', 'Regular'), ('plaintext', 'Plain Text'), ('absplit', 'AB Split'), ('rss', 'RSS'), ('variate', 'Variate')], default='regular', string="Type") def update_opt_out_ts(self, email, list_ids, value): if len(list_ids) > 0: model = self.env['mail.mass_mailing.contact'].with_context(active_test=False) records = model.search([('email', '=ilike', email), ('list_ids', 'in', list_ids)]) records.write({'opt_out': value}) @api.multi def action_schedule_date(self): self.ensure_one() action = self.env.ref('mailchimp.mass_mailing_schedule_date_action').read()[0] action['context'] = dict(self.env.context, default_mass_mailing_id=self.id) return action @api.model def fetch_send_to_activity(self): self.ensure_one() account = self.mailchimp_template_id.account_id if not account: return True count = 1000 offset = 0 sent_to_lists = [] while True: prepared_vals = {'count': count, 'offset': offset, 'fields': 'sent_to.status,sent_to.email_address'} response = account._send_request( 'reports/%s/sent-to' % self.mailchimp_id, {}, params=prepared_vals) if len(response.get('sent_to')) == 0: break if isinstance(response.get('sent_to'), dict): sent_to_lists += [response.get('sent_to')] sent_to_lists += response.get('sent_to') offset = offset + 1000 return sent_to_lists @api.multi def process_send_to_activity_report(self): self.ensure_one() stat_obj = self.env['mail.mail.statistics'] sent_to_lists = self.fetch_send_to_activity() if not sent_to_lists: return True domain = safe_eval(self.mailing_domain) contact_ids = self.env[self.mailing_model_real].search(domain) for record_dict in sent_to_lists: prepared_vals = {} email_address = record_dict.get('email_address') status = record_dict.get('status') if status == 'sent': prepared_vals.update({'sent': self.sent_date, 'scheduled': self.sent_date, 'bounced': False}) elif status in ['hard', 'soft']: prepared_vals.update({'bounced': self.sent_date, 'sent': self.sent_date, 'scheduled': self.sent_date}) existing = self.statistics_ids.filtered(lambda x: x.email == email_address) if existing: existing.write(prepared_vals) else: res_id = contact_ids.filtered(lambda x: x.email == email_address) prepared_vals.update({ 'model': self.mailing_model_real, 'res_id': res_id.id, 'mass_mailing_id': self.id, 'email': email_address, }) self.statistics_ids.create(prepared_vals) return sent_to_lists @api.multi def process_email_activity_report(self): self.ensure_one() self.process_send_to_activity_report() account = self.mailchimp_template_id.account_id count = 1000 offset = 0 email_lists = [] while True: prepared_vals = {'count': count, 'offset': offset, 'fields': 'emails.email_address,emails.activity'} response = account._send_request( 'reports/%s/email-activity' % self.mailchimp_id, {}, params=prepared_vals) if len(response.get('emails')) == 0: break if isinstance(response.get('emails'), dict): email_lists += [response.get('emails')] email_lists += response.get('emails') offset = offset + 1000 for email in email_lists: email_address = email.get('email_address') activities = email.get('activity') if not activities: continue prepared_vals = {} for acitvity in activities: action = acitvity.get('action') if action == 'open': prepared_vals.update({'opened': account.covert_date(acitvity.get('timestamp'))}) elif action == 'click': prepared_vals.update({'clicked': account.covert_date(acitvity.get('timestamp'))}) elif action == 'bounce': prepared_vals.update({'bounced': account.covert_date(acitvity.get('timestamp'))}) existing = self.statistics_ids.filtered(lambda x: x.email == email_address) if existing: existing.write(prepared_vals) @api.model def fetch_email_activity(self): stat_obj = self.env['mail.mail.statistics'] sent_date = datetime.today() - timedelta(days=30) sent_date = sent_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT) for record in self.search([('state', 'not in', ['draft', 'in_queue']), ('mailchimp_id', '!=', False), ('sent_date', '>=', sent_date)]): record.fetch_campaign() # record.process_email_activity_report() return True @api.multi def create_or_update_campaigns(self, values_dict, account=False): fetch_needed = False list_obj = self.env['mailchimp.lists'] template_obj = self.env['mailchimp.templates'] mailchimp_id = values_dict.get('id') settings_dict = values_dict.get('settings') recipients_dict = values_dict.get('recipients') list_id = recipients_dict.get('list_id') template_id = settings_dict.get('template_id') if list_id: list_obj = list_obj.search([('list_id', '=', list_id)]).odoo_list_id if template_id: template_obj = template_obj.search([('template_id', '=', template_id), ('account_id', '=', account.id)]) status = values_dict.get('status') subject_line = settings_dict.get('subject_line') or settings_dict.get('title') try: email_from = formataddr((settings_dict.get('from_name'), settings_dict.get('reply_to'))) except Exception as e: email_from = self.env['mail.message']._get_default_from() prepared_vals = { 'create_date': values_dict.get('create_time'), 'sent_date': values_dict.get('send_time'), 'name': subject_line, 'mailchimp_id': mailchimp_id, 'mailing_model_id': self.env.ref('mass_mailing.model_mail_mass_mailing_list').id, 'contact_list_ids': [(6, 0, list_obj.ids)], 'mailchimp_template_id': template_obj.id, 'mailchimp_champ_type': values_dict.get('type'), 'email_from': email_from, 'reply_to': email_from, } if status in ['save', 'paused']: prepared_vals.update({'state': 'draft'}) elif status == 'schedule': prepared_vals.update({'state': 'in_queue'}) elif status == 'sending': prepared_vals.update({'state': 'sending'}) elif status == 'sent': fetch_needed = True prepared_vals.update({'state': 'done'}) for item in DATE_CONVERSION: if prepared_vals.get(item, False) == '': prepared_vals[item] = False if prepared_vals.get(item, False): prepared_vals[item] = account.covert_date(prepared_vals.get(item)) existing_list = self.search([('mailchimp_id', '=', mailchimp_id)]) if not existing_list: existing_list = self.create(prepared_vals) self.env.cr.execute(""" UPDATE mail_mass_mailing SET create_date = '%s' WHERE id = %s """ % (prepared_vals.get('create_date'), existing_list.id)) else: existing_list.write(prepared_vals) existing_list._onchange_model_and_list() existing_list.body_html = False if fetch_needed: existing_list.process_email_activity_report() return True @api.multi def fetch_campaign(self): self.ensure_one() if not self.mailchimp_id: return True account = self.mailchimp_template_id.account_id params_vals = { 'fields': 'id,type,status,create_time,send_time,settings.template_id,settings.subject_line,settings.title,settings.from_name,settings.reply_to,recipients.list_id'} response = account._send_request('campaigns/%s' % self.mailchimp_id, {}, params=params_vals) self.create_or_update_campaigns(response, account=account) return True @api.multi def import_campaigns(self, account=False): if not account: raise Warning("MailChimp Account not defined to import Campaigns") count = 1000 offset = 0 campaigns_list = [] while True: prepared_vals = {'count': count, 'offset': offset} response = account._send_request('campaigns', {}, params=prepared_vals) if len(response.get('campaigns')) == 0: break if isinstance(response.get('campaigns'), dict): campaigns_list += [response.get('campaigns')] campaigns_list += response.get('campaigns') offset = offset + 1000 for campaigns_dict in campaigns_list: self.create_or_update_campaigns(campaigns_dict, account=account) return True @api.model def _prepare_vals_for_export(self): self.ensure_one() from_name, from_email = parseaddr(self.email_from) reply_to_name, reply_to_email = parseaddr(self.reply_to) settings_dict = {'subject_line': self.name, 'title': self.name, 'from_name': from_name, 'reply_to': reply_to_email, 'template_id': int(self.mailchimp_template_id.template_id)} prepared_vals = {'type': 'regular', 'recipients': {'list_id': self.contact_list_ids.mailchimp_list_id.list_id, }, 'settings': settings_dict} if self.mailchimp_segment_id.mailchimp_id: prepared_vals['recipients'].update({'segment_opts': {'saved_segment_id': int(self.mailchimp_segment_id.mailchimp_id)}}) return prepared_vals @api.one def export_to_mailchimp(self, account=False): if self.mailchimp_id: return True if not account: raise Warning("MailChimp Account not defined in selected Template.") prepared_vals = self._prepare_vals_for_export() response = account._send_request('campaigns', prepared_vals, method='POST') if response.get('id', False): self.write({'mailchimp_id': response['id']}) else: ValidationError(_("MailChimp Identification wasn't received. Please try again!")) self._cr.commit() return True @api.one def send_now_mailchimp(self, account=False): if not account: raise Warning("MailChimp Account not defined in selected Template.") response = account._send_request('campaigns/%s/actions/send' % self.mailchimp_id, {}, method='POST') return True @api.multi def send_test_mail_mailchimp(self, test_emails): self.ensure_one() self.export_to_mailchimp(self.mailchimp_template_id.account_id) prepared_vals = {'test_emails': test_emails, 'send_type': 'html'} response = self.mailchimp_template_id.account_id._send_request('campaigns/%s/actions/test' % self.mailchimp_id, prepared_vals, method='POST') return True @api.multi def schedule_mailchimp_champaign(self, schedule_date): self.ensure_one() self.export_to_mailchimp(self.mailchimp_template_id.account_id) prepared_vals = {'schedule_time': schedule_date.isoformat()} response = self.mailchimp_template_id.account_id._send_request( 'campaigns/%s/actions/schedule' % self.mailchimp_id, prepared_vals, method='POST') return True @api.multi def cancel_mass_mailing(self): res = super(MassMailing, self).cancel_mass_mailing() if self.mailchimp_id and self.mailchimp_template_id: self.mailchimp_template_id.account_id._send_request('campaigns/%s/actions/cancel-send' % self.mailchimp_id, {}, method='POST') if self.schedule_date: self.mailchimp_template_id.account_id._send_request( 'campaigns/%s/actions/unschedule' % self.mailchimp_id, {}, method='POST') return res @api.multi def put_in_queue(self): res = super(MassMailing, self).put_in_queue() for record in self.filtered(lambda x: x.mailchimp_template_id): if len(record.contact_list_ids) > 1: raise ValidationError(_("Multiple list is not allowed while going with MailChimp!")) if record.contact_list_ids.filtered(lambda x: not x.mailchimp_list_id): raise ValidationError(_("Please provide MailChimp list as you selected MailChimp Template!")) record.export_to_mailchimp(record.mailchimp_template_id.account_id) if record.mailchimp_id: record.send_now_mailchimp(record.mailchimp_template_id.account_id) record.process_send_to_activity_report() record.fetch_campaign() return res @api.model def _process_mass_mailing_queue(self): mass_mailings = self.search( [('state', 'in', ('in_queue', 'sending')), '|', ('schedule_date', '<', fields.Datetime.now()), ('schedule_date', '=', False)]) for mass_mailing in mass_mailings: user = mass_mailing.write_uid or self.env.user mass_mailing = mass_mailing.with_context(**user.sudo(user=user).context_get()) if mass_mailing.mailchimp_id: mass_mailing.fetch_campaign() continue if len(mass_mailing.get_remaining_recipients()) > 0: mass_mailing.state = 'sending' mass_mailing.send_mail() else: mass_mailing.write({'state': 'done', 'sent_date': fields.Datetime.now()}) @api.onchange('mailing_model_id', 'contact_list_ids') def _onchange_model_and_list(self): res = super(MassMailing, self)._onchange_model_and_list() mailing_domain = [] list_obj = self.env['mailchimp.lists'] list_ids = list_obj.search([('odoo_list_id', 'in', self.contact_list_ids.ids)]) self.mailchimp_list_id = list_ids and list_ids[0] or False if self.mailchimp_list_id: self.email_from = formataddr((self.mailchimp_list_id.from_name, self.mailchimp_list_id.from_email)) self.reply_to = formataddr((self.mailchimp_list_id.from_name, self.mailchimp_list_id.from_email)) return res