odoo/ext/custom-addons/mailchimp/models/mass_mailing.py

349 lines
16 KiB
Python

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