349 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			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
 |