Class: CampaignEmail
Overview
== Schema Information
Table name: campaign_actions
Database name: primary
id :integer not null, primary key
description :text
frequency :integer
last_transmitted :datetime
name :string
scheduled_time :datetime
sender_email :string
sequence :integer
state :string
type :string
created_at :datetime not null
updated_at :datetime not null
campaign_id :integer
creator_id :integer
email_template_id :integer
sender_id :integer
source_id :integer
updater_id :integer
Indexes
campaign_actions_campaign_id_idx (campaign_id)
campaign_actions_email_template_id_idx (email_template_id)
idx_type (type)
Foreign Keys
fk_rails_... (campaign_id => campaigns.id)
fk_rails_... (email_template_id => email_templates.id)
Constant Summary
collapse
- FREQUENCIES =
{ 'daily' => 86_400, 'weekly' => 604_800 }.freeze
- SPECIAL_SENDERS =
{ 'Social' => 'social@warmlyyours.com' }.freeze
- STATES =
:percentage_for_processed,
:percentage_for_deferred,
:percentage_for_delivered,
:percentage_for_open,
:percentage_for_click,
:percentage_for_bounce,
:percentage_for_dropped,
:percentage_for_spamreport,
:percentage_for_unsubscribe,
%w[
pending
queued
exception
suppressed
duplicate
deferred
dropped
bounced
sent
processed
delivered
opened
clicked
spammed
unsubscribed
].freeze
Models::Auditable::ALWAYS_IGNORED
Instance Attribute Summary collapse
#creator, #updater
Class Method Summary
collapse
Instance Method Summary
collapse
#all_skipped_columns, #audit_reference_data, #should_not_save_version, #stamp_record
ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation
#publish_event
Instance Attribute Details
#clone_email_template_id ⇒ Object
Returns the value of attribute clone_email_template_id.
41
42
43
|
# File 'app/models/campaign_email.rb', line 41
def clone_email_template_id
@clone_email_template_id
end
|
#name ⇒ Object
before_destroy :can_be_destroyed?
Validations:
85
|
# File 'app/models/campaign_email.rb', line 85
validates :email_template, :name, presence: true
|
#sender_email ⇒ Object
86
|
# File 'app/models/campaign_email.rb', line 86
validates :sender_email, presence: true
|
Class Method Details
.combined_states_for_select ⇒ Object
166
167
168
|
# File 'app/models/campaign_email.rb', line 166
def self.combined_states_for_select
STATES.map { |state| [state.to_s.titleize, state] }
end
|
.frequencies_select ⇒ Object
162
163
164
|
# File 'app/models/campaign_email.rb', line 162
def self.frequencies_select
FREQUENCIES.collect { |text, seconds| [text, seconds] }
end
|
.ready_to_be_transmitted ⇒ ActiveRecord::Relation<CampaignEmail>
A relation of CampaignEmails that are ready to be transmitted. Active Record Scope
92
|
# File 'app/models/campaign_email.rb', line 92
scope :ready_to_be_transmitted, -> { joins(:campaign).where(campaigns: { state: 'active' }, state: 'scheduled').where(CampaignEmail[:scheduled_time].lteq(Time.current)) }
|
.send_monthly_summary_email(date_start: nil, date_end: nil) ⇒ Object
150
151
152
153
154
155
156
157
158
159
160
|
# File 'app/models/campaign_email.rb', line 150
def self.send_monthly_summary_email(date_start: nil, date_end: nil)
date_start ||= Date.current.beginning_of_month
date_end ||= date_start.end_of_month
date_range = (date_start..date_end)
campaign_emails = CampaignEmail.where(last_transmitted: date_range).joins(:campaign).where('last_transmitted between ? and ? and exclude_from_monthly_report = false', Date.current.last_month.beginning_of_month.beginning_of_day,
Date.current.last_month.end_of_month.end_of_day).order('last_transmitted asc').to_a
return 'No emails sent last month' unless campaign_emails.present?
InternalMailer.campaign_summary(campaign_emails).deliver
end
|
.sender_options ⇒ Object
146
147
148
|
# File 'app/models/campaign_email.rb', line 146
def self.sender_options
(Employee.includes(:employee_account).active_employees.map(&:email_with_name) + CampaignEmail::SPECIAL_SENDERS.map { |name, email| "#{name} <#{email}>" }).sort_by { |s| s[0] }
end
|
Instance Method Details
74
|
# File 'app/models/campaign_email.rb', line 74
belongs_to :campaign, inverse_of: :campaign_emails, optional: true
|
#campaign_deliveries ⇒ ActiveRecord::Relation<CampaignDelivery>
77
|
# File 'app/models/campaign_email.rb', line 77
has_many :campaign_deliveries
|
#can_be_destroyed? ⇒ Boolean
190
191
192
193
194
195
196
197
|
# File 'app/models/campaign_email.rb', line 190
def can_be_destroyed?
if (unscheduled? || scheduled?) && campaign_deliveries.empty?
true
else
errors.add :base, 'cannot delete campaign email which has already been sent'
false
end
end
|
#can_be_sent? ⇒ Boolean
244
245
246
|
# File 'app/models/campaign_email.rb', line 244
def can_be_sent?
unscheduled? or scheduled?
end
|
#can_be_transmitted? ⇒ Boolean
240
241
242
|
# File 'app/models/campaign_email.rb', line 240
def can_be_transmitted?
scheduled_time.present? and scheduled_time <= Time.current
end
|
#communication_recipients ⇒ ActiveRecord::Relation<CommunicationRecipient>
78
|
# File 'app/models/campaign_email.rb', line 78
has_many :communication_recipients, through: :campaign_deliveries
|
#cost ⇒ Object
199
200
201
|
# File 'app/models/campaign_email.rb', line 199
def cost
communication_recipients.count * 0.0008 end
|
#delivery_funnel_counts ⇒ Object
Returns a monotonic funnel of counts for easier human interpretation.
Keys are: :total_recipients, :suppressed, :processed, :bounced, :delivered, :opened, :clicked, :unsubscribed, :spammed
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
|
# File 'app/models/campaign_email.rb', line 276
def delivery_funnel_counts
suppressed = campaign_deliveries.where(state: 'suppressed').count
processed_total = communication_recipients.count
total_recipients = suppressed + processed_total
bounced_total = communication_recipients.where(state: 'bounced').count
delivered_total = communication_recipients.where(state: %w[delivered opened clicked spammed unsubscribed]).count
opened_total = communication_recipients.where(state: %w[opened clicked]).count
clicked_total = communication_recipients.where(state: 'clicked').count
unsubscribed_total = communication_recipients.where(state: 'unsubscribed').count
spammed_total = communication_recipients.where(state: 'spammed').count
{
total_recipients: total_recipients,
suppressed: suppressed,
processed: processed_total,
bounced: bounced_total,
delivered: delivered_total,
opened: opened_total,
clicked: clicked_total,
unsubscribed: unsubscribed_total,
spammed: spammed_total
}
end
|
#delivery_percentages ⇒ Object
259
260
261
262
263
264
265
266
267
268
269
270
271
|
# File 'app/models/campaign_email.rb', line 259
def delivery_percentages
hsh = {}
total = total_deliveries_count
delivery_stats.each do |state, counter|
percentage = if counter > 0 && total > 0
((counter.to_f / total) * 100).round(2)
else
0.0
end
hsh[state] = percentage
end
hsh
end
|
#delivery_stats ⇒ Object
248
249
250
251
|
# File 'app/models/campaign_email.rb', line 248
def delivery_stats
hsh = view_campaign_deliveries.group(:combined_state).count
hsh.slice(*STATES) end
|
Validations:
75
|
# File 'app/models/campaign_email.rb', line 75
belongs_to :email_template, optional: true
|
#frequency_description ⇒ Object
209
210
211
|
# File 'app/models/campaign_email.rb', line 209
def frequency_description
frequency.nil? ? 'One time' : FREQUENCIES.detect { |_k, v| v == frequency }[0]
end
|
#generate_email_dynamic_subscribers ⇒ Object
217
218
219
|
# File 'app/models/campaign_email.rb', line 217
def generate_email_dynamic_subscribers
campaign.subscriber_lists.where(list_type: 'email_dynamic').find_each(&:generate_subscribers)
end
|
#generate_source ⇒ Object
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
|
# File 'app/models/campaign_email.rb', line 317
def generate_source
return if campaign&.source_id.nil?
s = Source.new
s.parent_id = campaign.source_id
s.name = name
s.referral_code = s.generate_ref_code
if s.save
self.source_id = s.id
true
else
errors.add(:base, "Unable to create source. Error: #{s.errors.full_messages}")
false
end
end
|
175
176
177
|
# File 'app/models/campaign_email.rb', line 175
def last_transmitted_formatted
last_transmitted.to_fs(:compact)
end
|
#prepare_campaign_deliveries ⇒ Object
221
222
223
224
225
226
227
228
229
230
231
232
233
|
# File 'app/models/campaign_email.rb', line 221
def prepare_campaign_deliveries
generate_email_dynamic_subscribers
records = campaign.subscribers.active.pluck(:id).map do |subscriber_id|
{
campaign_email_id: id,
subscriber_id: subscriber_id,
state: 'pending'
}
end
require 'activerecord-import/base'
require 'activerecord-import/active_record/adapters/postgresql_adapter'
CampaignDelivery.import records, validate: true, on_duplicate_key_ignore: true
end
|
#recipient_count ⇒ Object
179
180
181
|
# File 'app/models/campaign_email.rb', line 179
def recipient_count
communication_recipients.count
end
|
#roi ⇒ Object
203
204
205
206
207
|
# File 'app/models/campaign_email.rb', line 203
def roi return profit if cost.zero?
((profit - cost) / cost) * 100
end
|
#scheduled_time_description ⇒ Object
213
214
215
|
# File 'app/models/campaign_email.rb', line 213
def scheduled_time_description
frequency.nil? ? 'Scheduled Time' : 'Next Scheduled Time'
end
|
#send_emails ⇒ Object
235
236
237
238
|
# File 'app/models/campaign_email.rb', line 235
def send_emails
prepare_campaign_deliveries unless campaign_deliveries.present?
CampaignDeliveryWorker.perform_async
end
|
#sender ⇒ Object
170
171
172
173
|
# File 'app/models/campaign_email.rb', line 170
def sender
email = Mail::Address.new(sender_email).address
email.nil? ? nil : Employee.joins(:employee_account).where(accounts: { email: email }).first
end
|
#set_new_scheduled_time ⇒ Object
183
184
185
186
187
188
|
# File 'app/models/campaign_email.rb', line 183
def set_new_scheduled_time
return unless frequency.present?
update(scheduled_time: last_transmitted + frequency)
reschedule
end
|
76
|
# File 'app/models/campaign_email.rb', line 76
belongs_to :source, optional: true
|
#total_deliveries_count ⇒ Object
254
255
256
|
# File 'app/models/campaign_email.rb', line 254
def total_deliveries_count
delivery_stats.values.sum
end
|
#update_email_template ⇒ Object
313
314
315
|
# File 'app/models/campaign_email.rb', line 313
def update_email_template
email_template.description = "Template for campaign email #{name}"
end
|
#view_campaign_deliveries ⇒ ActiveRecord::Relation<ViewCampaignDelivery>
79
|
# File 'app/models/campaign_email.rb', line 79
has_many :view_campaign_deliveries
|