VRFS-736 progression emails first pass

This commit is contained in:
Jonathan Kolyer 2014-05-18 00:12:01 +00:00
parent 72110cb057
commit 6e7562ffc8
9 changed files with 279 additions and 23 deletions

View File

@ -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);

View File

@ -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"

View File

@ -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

View File

@ -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|

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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