VRFS-736 progression emails first pass
This commit is contained in:
parent
72110cb057
commit
6e7562ffc8
|
|
@ -1,2 +1,12 @@
|
|||
ALTER TABLE email_batches ADD COLUMN type VARCHAR(64) NOT NULL DEFAULT 'JamRuby::EmailBatch';
|
||||
ALTER TABLE email_batches ADD COLUMN sub_type VARCHAR(64);
|
||||
|
||||
ALTER TABLE email_batches ALTER COLUMN body DROP NOT NULL;
|
||||
ALTER TABLE email_batches ALTER COLUMN subject DROP NOT NULL;
|
||||
|
||||
ALTER TABLE email_batch_sets ADD COLUMN trigger_index INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE email_batch_sets ADD COLUMN sub_type VARCHAR(64);
|
||||
ALTER TABLE email_batch_sets ADD COLUMN user_id VARCHAR(64);
|
||||
|
||||
CREATE INDEX email_batch_sets_progress_idx ON email_batch_sets(user_id, sub_type);
|
||||
CREATE INDEX users_musician_email_idx ON users(subscribe_email, musician);
|
||||
|
|
|
|||
|
|
@ -138,6 +138,9 @@ require "jam_ruby/models/country"
|
|||
require "jam_ruby/models/region"
|
||||
require "jam_ruby/models/city"
|
||||
require "jam_ruby/models/email_batch"
|
||||
require "jam_ruby/models/email_batch_periodic"
|
||||
require "jam_ruby/models/email_batch_new_musician"
|
||||
require "jam_ruby/models/email_batch_progression"
|
||||
require "jam_ruby/models/email_batch_set"
|
||||
require "jam_ruby/models/email_error"
|
||||
require "jam_ruby/app/mailers/async_mailer"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
module JamRuby
|
||||
class ProgressMailer < ActionMailer::Base
|
||||
include SendGrid
|
||||
layout "user_mailer"
|
||||
|
||||
sendgrid_category :use_subject_lines
|
||||
sendgrid_unique_args :env => Environment.mode
|
||||
default :from => UserMailer::DEFAULT_SENDER
|
||||
|
||||
def progress_reminder(batch_set)
|
||||
sendgrid_recipients([user.email])
|
||||
sendgrid_substitute('@USERID', [user.id])
|
||||
mail(:to => batch_set.user.email,
|
||||
:subject => batch_set.subject,
|
||||
:title => batch_set.title,
|
||||
:template_name => batch_set.sub_type) do |format|
|
||||
format.text
|
||||
format.html
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
module JamRuby
|
||||
class EmailBatchNewMusician < EmailBatchPeriodic
|
||||
|
||||
BATCH_SIZE = 3
|
||||
BATCH_SIZE = 500
|
||||
|
||||
SINCE_WEEKS = 2
|
||||
|
||||
|
|
@ -12,12 +12,12 @@ module JamRuby
|
|||
"New musicians with good Internet connections to you have joined JamKazam!"
|
||||
end
|
||||
|
||||
def self.fetch_recipients(since=nil)
|
||||
def fetch_recipients(since=nil)
|
||||
since ||= Time.now - SINCE_WEEKS.weeks
|
||||
User.geocoded_users
|
||||
.email_opt_in
|
||||
.where(['created_at < ?', since])
|
||||
.find_in_batches(batch_size: self::BATCH_SIZE) do |users|
|
||||
.find_in_batches(batch_size: EmailBatchNewMusician::BATCH_SIZE) do |users|
|
||||
new_musicians = users.inject({}) do |hh, uu|
|
||||
if 0 < (nearby = uu.nearest_musicians).count
|
||||
hh[uu] = nearby
|
||||
|
|
@ -31,7 +31,7 @@ module JamRuby
|
|||
def deliver_batch_sets!
|
||||
self.opt_in_count = 0
|
||||
sent = 0
|
||||
self.class.fetch_recipients(self.time_since_last_batch(SINCE_WEEKS)) do |new_musicians|
|
||||
self.fetch_recipients(self.time_since_last_batch(SINCE_WEEKS)) do |new_musicians|
|
||||
self.opt_in_count += new_musicians.count
|
||||
self.email_batch_sets << (bset = EmailBatchSet.load_set(self, new_musicians.keys.map(&:id)))
|
||||
new_musicians.each do |uu, nearby|
|
||||
|
|
|
|||
|
|
@ -2,22 +2,25 @@ module JamRuby
|
|||
class EmailBatchPeriodic < EmailBatch
|
||||
self.abstract_class = true
|
||||
|
||||
def time_since_last_batch(default_weeks=2)
|
||||
if previous = self.class
|
||||
def time_since_last_batch_query
|
||||
self.class
|
||||
.where(['created_at < ?', self.created_at])
|
||||
.order('created_at DESC')
|
||||
.limit(1)
|
||||
.first
|
||||
end
|
||||
|
||||
def time_since_last_batch(default_weeks=2)
|
||||
if previous = self.time_since_last_batch_query.first
|
||||
return previous.created_at
|
||||
end
|
||||
Time.now - default_weeks.weeks
|
||||
end
|
||||
|
||||
def self.fetch_recipients(since=nil)
|
||||
def fetch_recipients(since=nil)
|
||||
yield([]) if block_given?
|
||||
end
|
||||
|
||||
def self.subject
|
||||
def self.subject(subtype=nil)
|
||||
''
|
||||
end
|
||||
|
||||
|
|
@ -27,7 +30,7 @@ module JamRuby
|
|||
|
||||
def self.new(*args)
|
||||
oo = super
|
||||
oo.body = nil
|
||||
oo.body = ''
|
||||
oo.subject = self.subject
|
||||
oo
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,139 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
module JamRuby
|
||||
class EmailBatchProgression < EmailBatchPeriodic
|
||||
|
||||
BATCH_SIZE = 3
|
||||
SINCE_WEEKS = 2
|
||||
|
||||
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
|
||||
:reg_notinvite, # Registered Musician Has Not Invited Friends to Join JamKazam
|
||||
:reg_notconnect, # Registered Musician Has Not Connected with any Friends on JamKazam
|
||||
:reg_notlike, # Registered Musician Has Not Liked Jamkazam
|
||||
:sess_notrecord # Registered Musician Has Participated In a "Real" Session But Has Not Made a Recording
|
||||
]
|
||||
|
||||
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
|
||||
|
||||
def days_past_for_trigger_index(idx)
|
||||
self.class.subtype_trigger_interval(self.sub_type)[idx]
|
||||
end
|
||||
|
||||
def fetch_recipients(since=nil)
|
||||
since ||= Time.now - SINCE_WEEKS.weeks
|
||||
User.geocoded_users
|
||||
.email_opt_in
|
||||
.where(['created_at < ?', since])
|
||||
.find_in_batches(batch_size: self::BATCH_SIZE) do |users|
|
||||
new_musicians = users.inject({}) do |hh, uu|
|
||||
if 0 < (nearby = uu.nearest_musicians).count
|
||||
hh[uu] = nearby
|
||||
end
|
||||
hh
|
||||
end
|
||||
yield(new_musicians) if block_given?
|
||||
end
|
||||
end
|
||||
|
||||
def trigger_date_constraint(trigger_idx, date_column)
|
||||
intervals = self.class.subtype_trigger_interval(self.sub_type)
|
||||
case trigger_idx
|
||||
when 1
|
||||
return ["#{date_column} >= ? AND #{date_column} < ?",
|
||||
Time.now - intervals[0].days,
|
||||
Time.now - intervals[1].days]
|
||||
else
|
||||
return ["#{date_column} < ?", Time.now - intervals[trigger_idx].days]
|
||||
end
|
||||
end
|
||||
|
||||
def fetch_client_notdl(trigger_idx=0)
|
||||
fetched = []
|
||||
rel = User
|
||||
.joins("LEFT JOIN email_batch_sets AS ebs ON ebs.user_id = users.id AND ebs.sub_type = '#{self.sub_type}'")
|
||||
.email_opt_in
|
||||
.musicians
|
||||
.where("ebs.id IS NULL OR (SELECT MAX(trigger_index) FROM email_batch_sets ebs WHERE ebs.sub_type = '#{self.sub_type}' AND ebs.user_id = users.id ) < #{trigger_idx}")
|
||||
.where("first_downloaded_client_at IS NULL")
|
||||
.where(self.trigger_date_constraint(trigger_idx, "users.created_at"))
|
||||
.find_in_batches(batch_size: 100) do |users|
|
||||
block_given? ? yield(users) : fetched.concat(users)
|
||||
end
|
||||
fetched
|
||||
end
|
||||
|
||||
def deliver_batch_sets!
|
||||
self.opt_in_count = 0
|
||||
sent = 0
|
||||
3.times do |trigger_idx|
|
||||
self.send("fetch_#{self.sub_type}", trigger_idx) do |users|
|
||||
users.each do |uu|
|
||||
self.email_batch_sets << (bset = EmailBatchSet.progress_set(self, uu, trigger_idx))
|
||||
ProgressMailer.send_reminder(bset).deliver
|
||||
sent += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
self.sent_count = sent
|
||||
self.save
|
||||
self.did_batch_run!
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -3,6 +3,7 @@ module JamRuby
|
|||
self.table_name = "email_batch_sets"
|
||||
|
||||
belongs_to :email_batch, :class_name => 'JamRuby::EmailBatch'
|
||||
belongs_to :user, :class_name => 'JamRuby::User'
|
||||
|
||||
def self.load_set(batch, user_ids)
|
||||
bset = self.new
|
||||
|
|
@ -14,5 +15,31 @@ module JamRuby
|
|||
bset
|
||||
end
|
||||
|
||||
def self.progress_set(batch, user, trigger_idx)
|
||||
bset = self.new
|
||||
bset.email_batch = batch
|
||||
bset.user = user
|
||||
bset.started_at = Time.now
|
||||
bset.batch_count = 1
|
||||
bset.trigger_index = trigger_idx
|
||||
bset.sub_type = batch.sub_type
|
||||
bset.save!
|
||||
bset
|
||||
end
|
||||
|
||||
def subject
|
||||
unless sub_type.blank?
|
||||
return EmailBatchProgression.subject(self.sub_type)
|
||||
end
|
||||
''
|
||||
end
|
||||
|
||||
def title
|
||||
unless sub_type.blank?
|
||||
return EmailBatchProgression.title(self.sub_type)
|
||||
end
|
||||
''
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -446,7 +446,9 @@ FactoryGirl.define do
|
|||
end
|
||||
|
||||
factory :email_batch_new_musician, :class => JamRuby::EmailBatchNewMusician do
|
||||
end
|
||||
|
||||
factory :email_batch_progression, :class => JamRuby::EmailBatchProgression do
|
||||
end
|
||||
|
||||
factory :notification, :class => JamRuby::Notification do
|
||||
|
|
|
|||
|
|
@ -1,23 +1,31 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe EmailBatch do
|
||||
let (:email_batch) { FactoryGirl.create(:email_batch) }
|
||||
let (:new_musician_batch) { FactoryGirl.create(:email_batch_new_musician) }
|
||||
|
||||
before(:each) do
|
||||
BatchMailer.deliveries.clear
|
||||
end
|
||||
describe 'all users' do
|
||||
let (:email_batch) { FactoryGirl.create(:email_batch) }
|
||||
|
||||
it 'has test emails setup' do
|
||||
pending
|
||||
expect(email_batch.test_emails.present?).to be true
|
||||
expect(email_batch.pending?).to be true
|
||||
before { pending }
|
||||
|
||||
users = email_batch.test_users
|
||||
expect(email_batch.test_count).to eq(users.count)
|
||||
before(:each) do
|
||||
BatchMailer.deliveries.clear
|
||||
end
|
||||
|
||||
it 'has test emails setup' do
|
||||
|
||||
expect(email_batch.test_emails.present?).to be true
|
||||
expect(email_batch.pending?).to be true
|
||||
|
||||
users = email_batch.test_users
|
||||
expect(email_batch.test_count).to eq(users.count)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'new musician' do
|
||||
let (:new_musician_batch) { FactoryGirl.create(:email_batch_new_musician) }
|
||||
|
||||
before { pending }
|
||||
|
||||
before(:each) do
|
||||
@u1 = FactoryGirl.create(:user, :lat => 37.791649, :lng => -122.394395, :email => 'jonathan@jamkazam.com', :subscribe_email => true, :created_at => Time.now - 3.weeks)
|
||||
@u2 = FactoryGirl.create(:user, :lat => 37.791649, :lng => -122.394395, :subscribe_email => true)
|
||||
|
|
@ -26,7 +34,7 @@ describe EmailBatch do
|
|||
end
|
||||
|
||||
it 'find new musicians with good score' do
|
||||
EmailBatchNewMusician.fetch_recipients do |new_musicians|
|
||||
new_musician_batch.fetch_recipients do |new_musicians|
|
||||
expect(new_musicians.count).to eq(2)
|
||||
num = (new_musicians.keys.map(&:id) - [@u1.id, @u4.id]).count
|
||||
expect(num).to eq(0)
|
||||
|
|
@ -34,7 +42,6 @@ describe EmailBatch do
|
|||
end
|
||||
|
||||
it 'has correct time since last batch' do
|
||||
pending
|
||||
tt = EmailBatchNewMusician.time_since_last_batch
|
||||
expect(tt.to_i).to be < (Time.now - 1.week).to_i
|
||||
end
|
||||
|
|
@ -49,4 +56,46 @@ describe EmailBatch do
|
|||
|
||||
end
|
||||
|
||||
context 'user progress' do
|
||||
|
||||
describe 'client not downloaded' do
|
||||
let(:client_notdl) { FactoryGirl.create(:email_batch_progression, :sub_type => :client_notdl) }
|
||||
|
||||
let(:user_client_notdl) { FactoryGirl.create(:user) }
|
||||
let(:user_client_notdl_trigger0) {
|
||||
FactoryGirl.create(:user,
|
||||
:created_at => Time.now - client_notdl.days_past_for_trigger_index(0).days)
|
||||
}
|
||||
let(:user_client_notdl_trigger1) {
|
||||
FactoryGirl.create(:user,
|
||||
:created_at => Time.now - client_notdl.days_past_for_trigger_index(1).days)
|
||||
}
|
||||
let(:user_client_notdl_trigger2) {
|
||||
FactoryGirl.create(:user,
|
||||
:created_at => Time.now - client_notdl.days_past_for_trigger_index(2).days)
|
||||
}
|
||||
|
||||
it 'returns no users' do
|
||||
pending
|
||||
user_client_notdl
|
||||
expect(client_notdl.fetch_client_notdl.count).to eq(0)
|
||||
end
|
||||
|
||||
it 'returns user first trigger' do
|
||||
user_client_notdl_trigger0
|
||||
expect(client_notdl.fetch_client_notdl.count).to eq(1)
|
||||
end
|
||||
|
||||
it 'returns user second trigger' do
|
||||
end
|
||||
|
||||
it 'returns user third trigger' do
|
||||
end
|
||||
|
||||
it 'returns no users after third trigger' do
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue