2014-05-18 00:12:01 +00:00
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
module JamRuby
|
|
|
|
|
|
class EmailBatchProgression < EmailBatchPeriodic
|
|
|
|
|
|
|
2014-05-21 22:41:17 +00:00
|
|
|
|
BATCH_SIZE = 500
|
2014-05-18 00:12:01 +00:00
|
|
|
|
SINCE_WEEKS = 2
|
2014-06-03 04:21:00 +00:00
|
|
|
|
NUM_IDX = 3
|
2014-05-18 00:12:01 +00:00
|
|
|
|
|
2014-05-19 04:35:25 +00:00
|
|
|
|
SUBTYPES = [:client_notdl, # Registered Musician Has Not Downloaded Client
|
|
|
|
|
|
:client_dl_notrun, # Registered Musician Has Downloaded Client But Not Yet Run It
|
|
|
|
|
|
:client_run_notgear, # Registered Musician Has Run Client But Not Successfully Qualified Audio Gear
|
|
|
|
|
|
:gear_notsess, # Registered Musician Has Successfully Qualified Audio Gear But Has Not Participated in a ‘Real’ Session
|
|
|
|
|
|
:sess_notgood, # Registered Musician Has Participated In a "Real" Session But Has Not Had a "Good" Session
|
|
|
|
|
|
:sess_notrecord, # Registered Musician Has Participated In a "Real" Session But Has Not Made a Recording
|
2014-05-18 00:12:01 +00:00
|
|
|
|
:reg_notinvite, # Registered Musician Has Not Invited Friends to Join JamKazam
|
2014-05-19 04:35:25 +00:00
|
|
|
|
:reg_notconnect, # Registered Musician Has Not Connected with any Friends on JamKazam
|
|
|
|
|
|
:reg_notlike, # Registered Musician Has Not Liked Jamkazam
|
2014-05-18 00:12:01 +00:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
SUBTYPE_METADATA = {
|
|
|
|
|
|
:client_notdl => {
|
|
|
|
|
|
:subject => "Get the free JamKazam app now and start playing with others!",
|
|
|
|
|
|
:title => "Download the Free JamKazam App"
|
|
|
|
|
|
},
|
|
|
|
|
|
:client_dl_notrun => {
|
|
|
|
|
|
:subject => "Having trouble running the JamKazam application?",
|
|
|
|
|
|
:title => "Running the JamKazam App"
|
|
|
|
|
|
},
|
|
|
|
|
|
:client_run_notgear => {
|
|
|
|
|
|
:subject => "Having trouble setting up your audio gear for JamKazam?",
|
|
|
|
|
|
:title => "Setting Up and Qualifying Your Audio Gear on JamKazam"
|
|
|
|
|
|
},
|
|
|
|
|
|
:gear_notsess => {
|
|
|
|
|
|
:subject => "Having trouble getting into a session with other musicians?",
|
|
|
|
|
|
:title => "Tips on Getting into Sessions with Other Musicians"
|
|
|
|
|
|
},
|
|
|
|
|
|
:sess_notgood => {
|
|
|
|
|
|
:subject => "Have you played in a “good” session on JamKazam yet?",
|
|
|
|
|
|
:title => "Having a Good Session on JamKazam"
|
|
|
|
|
|
},
|
|
|
|
|
|
:reg_notinvite => {
|
|
|
|
|
|
:subject => "Invite your friends to JamKazam, best way to play online!",
|
|
|
|
|
|
:title => "Invite Your Friends to Come and Play with You"
|
|
|
|
|
|
},
|
|
|
|
|
|
:reg_notconnect => {
|
|
|
|
|
|
:subject => "Make friends on JamKazam and play more music!",
|
|
|
|
|
|
:title => "Connecting with Friends on JamKazam"
|
|
|
|
|
|
},
|
|
|
|
|
|
:reg_notlike => {
|
|
|
|
|
|
:subject => "Please give us a LIKE!",
|
|
|
|
|
|
:title => "Like/Follow JamKazam"
|
|
|
|
|
|
},
|
|
|
|
|
|
:sess_notrecord => {
|
|
|
|
|
|
:subject => "Want to make recordings during your JamKazam sessions?",
|
|
|
|
|
|
:title => "Making & Sharing Recordings on JamKazam"
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def self.subject(subtype=nil)
|
|
|
|
|
|
SUBTYPE_METADATA[subtype][:subject] if subtype
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
def self.title(subtype=nil)
|
|
|
|
|
|
SUBTYPE_METADATA[subtype][:title] if subtype
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
def self.subtype_trigger_interval(subtype)
|
|
|
|
|
|
[:reg_notlike, :sess_notrecord].include?(subtype) ? [7,14,21] : [2,5,14]
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
def self.days_past_for_trigger_index(subtype, idx)
|
|
|
|
|
|
self.subtype_trigger_interval(subtype)[idx]
|
|
|
|
|
|
end
|
|
|
|
|
|
|
2014-08-05 12:09:12 +00:00
|
|
|
|
def self.min_create_date
|
|
|
|
|
|
min = self.unscoped
|
|
|
|
|
|
.where(:aasm_state => 'delivered')
|
|
|
|
|
|
.order('created_at ASC')
|
|
|
|
|
|
.limit(1)
|
|
|
|
|
|
.pluck(:created_at)
|
|
|
|
|
|
.first || Time.now
|
|
|
|
|
|
min.to_date.strftime('%Y-%m-%d')
|
|
|
|
|
|
end
|
|
|
|
|
|
|
2014-05-20 05:21:11 +00:00
|
|
|
|
def days_diff(tidx)
|
|
|
|
|
|
self.class.days_past_for_trigger_index(self.sub_type,tidx) - self.class.days_past_for_trigger_index(self.sub_type, tidx - 1)
|
|
|
|
|
|
end
|
|
|
|
|
|
|
2014-05-18 00:12:01 +00:00
|
|
|
|
def days_past_for_trigger_index(idx)
|
|
|
|
|
|
self.class.subtype_trigger_interval(self.sub_type)[idx]
|
|
|
|
|
|
end
|
|
|
|
|
|
|
2014-05-19 03:23:14 +00:00
|
|
|
|
def make_set(uu, trigger_idx)
|
|
|
|
|
|
EmailBatchSet.progress_set(self, uu, trigger_idx)
|
2014-05-18 00:12:01 +00:00
|
|
|
|
end
|
2014-05-20 05:21:11 +00:00
|
|
|
|
|
|
|
|
|
|
def trigger_date_constraint(trigger_idx, tbl='tt')
|
2014-05-18 00:12:01 +00:00
|
|
|
|
intervals = self.class.subtype_trigger_interval(self.sub_type)
|
2014-05-20 05:21:11 +00:00
|
|
|
|
date_constraint = (Time.now - intervals[trigger_idx].days + 1.hour).strftime('%Y-%m-%d %H:%M:%S')
|
2014-05-19 15:12:59 +00:00
|
|
|
|
|
2014-05-19 03:23:14 +00:00
|
|
|
|
case self.sub_type.to_sym
|
2014-05-19 15:12:59 +00:00
|
|
|
|
when :client_notdl, :reg_notconnect, :reg_notinvite, :reg_notlike
|
2014-05-20 05:21:11 +00:00
|
|
|
|
return "#{tbl}.created_at < '#{date_constraint}'"
|
2014-05-19 04:35:25 +00:00
|
|
|
|
when :client_dl_notrun
|
2014-05-20 05:21:11 +00:00
|
|
|
|
return "#{tbl}.first_downloaded_client_at < '#{date_constraint}'"
|
2014-05-19 04:35:25 +00:00
|
|
|
|
when :client_run_notgear
|
2014-05-20 05:21:11 +00:00
|
|
|
|
return "#{tbl}.first_ran_client_at < '#{date_constraint}'"
|
2014-05-19 04:35:25 +00:00
|
|
|
|
when :gear_notsess
|
2014-05-20 05:21:11 +00:00
|
|
|
|
return "#{tbl}.first_certified_gear_at < '#{date_constraint}'"
|
2014-05-19 04:35:25 +00:00
|
|
|
|
when :sess_notgood
|
2014-05-20 05:21:11 +00:00
|
|
|
|
return "#{tbl}.first_real_music_session_at < '#{date_constraint}'"
|
2014-05-19 04:35:25 +00:00
|
|
|
|
when :sess_notrecord
|
2014-05-20 05:21:11 +00:00
|
|
|
|
return "#{tbl}.first_real_music_session_at < '#{date_constraint}'"
|
2014-05-19 03:23:14 +00:00
|
|
|
|
end
|
2014-05-18 00:12:01 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
2014-05-20 05:21:11 +00:00
|
|
|
|
def progress_column_constraint(tbl='tt')
|
2014-05-19 03:23:14 +00:00
|
|
|
|
case self.sub_type.to_sym
|
|
|
|
|
|
when :client_notdl
|
2014-05-20 05:21:11 +00:00
|
|
|
|
return "#{tbl}.first_downloaded_client_at IS NULL"
|
2014-05-19 04:35:25 +00:00
|
|
|
|
when :client_dl_notrun
|
2014-05-20 05:21:11 +00:00
|
|
|
|
return "#{tbl}.first_downloaded_client_at IS NOT NULL AND #{tbl}.first_ran_client_at IS NULL"
|
2014-05-19 04:35:25 +00:00
|
|
|
|
when :client_run_notgear
|
2014-05-20 05:21:11 +00:00
|
|
|
|
return "#{tbl}.first_ran_client_at IS NOT NULL AND #{tbl}.first_certified_gear_at IS NULL"
|
2014-05-19 04:35:25 +00:00
|
|
|
|
when :gear_notsess
|
2014-05-20 05:21:11 +00:00
|
|
|
|
return "#{tbl}.first_certified_gear_at IS NOT NULL AND #{tbl}.first_real_music_session_at IS NULL"
|
2014-05-19 04:35:25 +00:00
|
|
|
|
when :sess_notgood
|
2014-05-20 05:21:11 +00:00
|
|
|
|
return "#{tbl}.first_real_music_session_at IS NOT NULL AND #{tbl}.first_good_music_session_at IS NULL"
|
2014-05-19 04:35:25 +00:00
|
|
|
|
when :sess_notrecord
|
2014-05-20 05:21:11 +00:00
|
|
|
|
return "#{tbl}.first_real_music_session_at IS NOT NULL AND #{tbl}.first_recording_at IS NULL"
|
2014-05-19 04:35:25 +00:00
|
|
|
|
when :reg_notinvite
|
2014-05-21 06:20:15 +00:00
|
|
|
|
return "#{tbl}.first_invited_at IS NULL"
|
2014-05-19 04:35:25 +00:00
|
|
|
|
when :reg_notconnect
|
2014-05-21 06:20:15 +00:00
|
|
|
|
return "#{tbl}.first_friended_at IS NULL"
|
2014-05-19 04:35:25 +00:00
|
|
|
|
when :reg_notlike
|
2014-05-21 06:20:15 +00:00
|
|
|
|
return "#{tbl}.first_social_promoted_at IS NULL"
|
2014-05-19 03:23:14 +00:00
|
|
|
|
end
|
|
|
|
|
|
''
|
|
|
|
|
|
end
|
2014-05-19 00:43:37 +00:00
|
|
|
|
|
2014-05-21 22:41:17 +00:00
|
|
|
|
def fetch_recipients(trigger_idx=0, per_page=BATCH_SIZE)
|
2014-05-18 00:12:01 +00:00
|
|
|
|
fetched = []
|
2014-05-20 05:21:11 +00:00
|
|
|
|
offset = 0
|
2014-05-20 05:58:06 +00:00
|
|
|
|
if 0==trigger_idx
|
|
|
|
|
|
prev_date_sql = 'tt.prev_trigger_date IS NULL'
|
|
|
|
|
|
else
|
|
|
|
|
|
prev_date_sql = "tt.prev_trigger_date + interval '#{self.days_diff(trigger_idx)} days' <= '#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}'"
|
|
|
|
|
|
end
|
|
|
|
|
|
countsql =<<SQL
|
|
|
|
|
|
SELECT COUNT(*)
|
|
|
|
|
|
FROM (SELECT "users".*,
|
|
|
|
|
|
(SELECT COALESCE(MAX(ebs.trigger_index),-1)
|
|
|
|
|
|
FROM email_batch_sets ebs
|
|
|
|
|
|
WHERE
|
|
|
|
|
|
ebs.sub_type = '#{self.sub_type}' AND
|
|
|
|
|
|
ebs.user_id = users.id
|
|
|
|
|
|
) AS tidx,
|
|
|
|
|
|
(SELECT created_at
|
|
|
|
|
|
FROM email_batch_sets ebs
|
|
|
|
|
|
WHERE
|
|
|
|
|
|
ebs.sub_type = '#{self.sub_type}' AND
|
|
|
|
|
|
ebs.user_id = users.id AND trigger_index = #{trigger_idx - 1}
|
|
|
|
|
|
) AS prev_trigger_date
|
|
|
|
|
|
FROM users) tt
|
|
|
|
|
|
WHERE
|
|
|
|
|
|
tt."subscribe_email" = 't' AND
|
|
|
|
|
|
tt."musician" = 't' AND
|
|
|
|
|
|
(#{progress_column_constraint}) AND
|
|
|
|
|
|
(#{self.trigger_date_constraint(trigger_idx)}) AND
|
|
|
|
|
|
tt.tidx = #{trigger_idx - 1} AND
|
|
|
|
|
|
#{prev_date_sql}
|
|
|
|
|
|
SQL
|
|
|
|
|
|
num_users = User.count_by_sql(countsql)
|
|
|
|
|
|
loops = (num_users / per_page) + (num_users % per_page) - 1
|
|
|
|
|
|
0.upto(loops) do |nn|
|
|
|
|
|
|
offset = nn * per_page
|
|
|
|
|
|
sql =<<SQL
|
|
|
|
|
|
SELECT tt.*
|
2014-05-20 05:21:11 +00:00
|
|
|
|
FROM (SELECT "users".*,
|
|
|
|
|
|
(SELECT COALESCE(MAX(ebs.trigger_index),-1)
|
|
|
|
|
|
FROM email_batch_sets ebs
|
|
|
|
|
|
WHERE
|
|
|
|
|
|
ebs.sub_type = '#{self.sub_type}' AND
|
|
|
|
|
|
ebs.user_id = users.id
|
|
|
|
|
|
) AS tidx,
|
|
|
|
|
|
(SELECT created_at
|
|
|
|
|
|
FROM email_batch_sets ebs
|
|
|
|
|
|
WHERE
|
|
|
|
|
|
ebs.sub_type = '#{self.sub_type}' AND
|
|
|
|
|
|
ebs.user_id = users.id AND trigger_index = #{trigger_idx - 1}
|
|
|
|
|
|
) AS prev_trigger_date
|
|
|
|
|
|
FROM users) tt
|
|
|
|
|
|
WHERE
|
|
|
|
|
|
tt."subscribe_email" = 't' AND
|
|
|
|
|
|
tt."musician" = 't' AND
|
|
|
|
|
|
(#{progress_column_constraint}) AND
|
|
|
|
|
|
(#{self.trigger_date_constraint(trigger_idx)}) AND
|
|
|
|
|
|
tt.tidx = #{trigger_idx - 1} AND
|
|
|
|
|
|
#{prev_date_sql}
|
|
|
|
|
|
ORDER BY tt.id
|
|
|
|
|
|
ASC LIMIT #{per_page}
|
|
|
|
|
|
OFFSET #{offset}
|
|
|
|
|
|
SQL
|
2014-05-20 05:58:06 +00:00
|
|
|
|
users = User.find_by_sql(sql)
|
|
|
|
|
|
block_given? ? yield(users) : fetched.concat(users)
|
|
|
|
|
|
end
|
2014-05-18 00:12:01 +00:00
|
|
|
|
fetched
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
def deliver_batch_sets!
|
|
|
|
|
|
self.opt_in_count = 0
|
|
|
|
|
|
sent = 0
|
|
|
|
|
|
3.times do |trigger_idx|
|
2014-05-19 03:23:14 +00:00
|
|
|
|
self.fetch_recipients(trigger_idx) do |users|
|
2014-05-18 00:12:01 +00:00
|
|
|
|
users.each do |uu|
|
2014-05-18 05:24:07 +00:00
|
|
|
|
self.email_batch_sets << (bset = self.make_set(uu, trigger_idx))
|
2016-07-17 15:16:27 +00:00
|
|
|
|
ProgressMailer.send_reminder(bset).deliver_now
|
2014-05-18 00:12:01 +00:00
|
|
|
|
sent += 1
|
|
|
|
|
|
end
|
|
|
|
|
|
end
|
|
|
|
|
|
end
|
|
|
|
|
|
self.sent_count = sent
|
|
|
|
|
|
self.save
|
|
|
|
|
|
self.did_batch_run!
|
|
|
|
|
|
end
|
|
|
|
|
|
|
2014-05-21 22:41:17 +00:00
|
|
|
|
def self.send_progress_batch
|
|
|
|
|
|
self::SUBTYPES.collect do |stype|
|
|
|
|
|
|
ebatch = self.create
|
|
|
|
|
|
ebatch.update_attribute(:sub_type, stype.to_s)
|
|
|
|
|
|
ebatch
|
|
|
|
|
|
end.each do |ebatch|
|
|
|
|
|
|
ebatch.deliver_batch
|
|
|
|
|
|
end
|
|
|
|
|
|
end
|
|
|
|
|
|
|
2014-05-18 00:12:01 +00:00
|
|
|
|
end
|
|
|
|
|
|
end
|