VRFS-4748 - 3 differet email reminders

This commit is contained in:
Seth Call 2018-05-20 21:57:53 -05:00
parent 88c23d3f8a
commit b0ccb5202e
17 changed files with 419 additions and 7 deletions

View File

@ -387,4 +387,5 @@ teacher_search_control.sql
user_timezone.sql
onboarder_limit.sql
onboarding_emails.sql
limit_counter_reminders.sql
limit_counter_reminders.sql
amazon_v2.sql

9
db/up/amazon_v2.sql Normal file
View File

@ -0,0 +1,9 @@
-- create new lesson package types
ALTER TABLE users ADD COLUMN first_lesson_booked_at TIMESTAMP WITHOUT TIME ZONE;
ALTER TABLE users ADD COLUMN remind_take_lesson_times INTEGER NOT NULL DEFAULT 0;
ALTER TABLE users ADD COLUMN remind_take_lesson_at TIMESTAMP WITHOUT TIME ZONE;
CREATE INDEX index_remind_take_lesson_times ON users USING btree(remind_take_lesson_times);
-- bootstrap first_lesson_booked_at
UPDATE users SET first_lesson_booked_at = (SELECT lesson_sessions.created_at FROM lesson_sessions WHERE users.id = lesson_sessions.user_id ORDER BY lesson_sessions.created_at LIMIT 1);

View File

@ -2167,5 +2167,40 @@ module JamRuby
format.html
end
end
def amazon_prompt_take_lesson(user, teacher, attempt)
@user = user
@attempt = attempt
@teacher = teacher
@biography = teacher.teacher.biography
if @biography.nil?
@biography = ''
else
@biography = @biography.truncate(160)
end
email = user.email
if attempt == 0
@subject = "Scheduling your first free lesson"
elsif attempt == 1
@subject = "Don't forget to book your first free lesson"
else
@subject = "Last reminder to schedule your first free lesson!"
end
unique_args = {:type => "amazon_prompt_take_lesson"}
sendgrid_category "amazon_prompt_take_lesson"
sendgrid_unique_args :type => unique_args[:type]
sendgrid_recipients([email])
mail(:to => email, :subject => @subject) do |format|
format.text
format.html {render :layout => "raw_mailer"}
end
end
end
end

View File

@ -0,0 +1,92 @@
<% provide(:title, @subject) %>
<% if @attempt == 0 %>
<p style="margin-top:0px"><font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif">
Welcome to JamKazam, and congratulations on your free private music lessons, courtesy of Amazon! Here is a great instructor we can recommend to you:
</font></p>
<br>
<table>
<tr>
<td width="58" align="left" valign="top">
<p style="padding:2px;width:54px;height:54px;background-color:#ed3618;-webkit-border-radius:28px;-moz-border-radius:28px;border-radius:28px;margin-right:10px">
<img src="<%= @teacher.resolved_photo_url %>" width="54" height="54" style="-webkit-border-radius:26px;-moz-border-radius:26px;border-radius:26px;">
</p><font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif"><%= @teacher.name %></font></td>
<td><p><font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif"><%= @biography %></font></p>
</td>
</tr>
</table>
<p style="text-align:center"><font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif"><a href="<%= %>"style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">SCHEDULE FIRST FREE LESSON NOW</a></font></p>
<br>
<p><font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif">Click the button to schedule your first free lesson with the instructor listed above! Or if you think you'd prefer a different instructor, <a style="color: #ffcc00;" href="<%= User.search_url %>">click here</a> to search our guitar and bass instructors to select a different one.</font></p>
<p><font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif">To take online lessons, you'll need:</font></p>
<font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif">
<ul>
<li><font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif">A Windows or Mac computer</font></li>
<li><font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif">A webcam, a microphone, and a port to connect headphones or earbuds (all three are usually built into your computer)</font></li>
<li><font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif">Internet service at your home</font></li>
</ul>
</font>
<p><font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif">Schedule your first free lesson now to get started. And if you have any questions, don't hesitate to contact us by email at <a href="mailto:support@jamkazam.com" style="color: #ffcc00;">support@jamkazam.com</a>. We are here to make your musical journey a rewarding one!</font></p>
<% elsif @attempt == 1 %>
<p style="margin-top:0px"><font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif">
Just a quick reminder - don't forget to schedule your first free lesson! Here is another great instructor we can recommend to you:
</font></p>
<br>
<table>
<tr>
<td width="58" align="left" valign="top">
<p style="padding:2px;width:54px;height:54px;background-color:#ed3618;-webkit-border-radius:28px;-moz-border-radius:28px;border-radius:28px;margin-right:10px">
<img src="<%= @teacher.resolved_photo_url %>" width="54" height="54" style="-webkit-border-radius:26px;-moz-border-radius:26px;border-radius:26px;">
</p><font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif"><%= @teacher.name %></font></td>
<td><p><font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif"><%= @biography %></font></p>
</td>
</tr>
</table>
<p style="text-align:center"><font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif"><a href="<%= %>"style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">SCHEDULE FIRST FREE LESSON NOW</a></font></p>
<br>
<p><font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif">Click the button to schedule your first free lesson with the instructor listed above! Or if you think you'd prefer a different instructor, <a style="color: #ffcc00;" href="<%= User.search_url %>">click here</a> to search our guitar and bass instructors to select a different one.</font></p>
<p><font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif">If you have any questions, don't hesitate to contact us by email at <a href="mailto:support@jamkazam.com" style="color: #ffcc00;">support@jamkazam.com</a>.</font></p>
<% else %>
<p style="margin-top:0px"><font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif">
We don't want to bother you, so just sending one more reminder. A private instructor is by far the best way to learn a new instrument. Here is another great instructor we can recommend to you:
</font></p>
<br>
<table>
<tr>
<td width="58" align="left" valign="top">
<p style="padding:2px;width:54px;height:54px;background-color:#ed3618;-webkit-border-radius:28px;-moz-border-radius:28px;border-radius:28px;margin-right:10px">
<img src="<%= @teacher.resolved_photo_url %>" width="54" height="54" style="-webkit-border-radius:26px;-moz-border-radius:26px;border-radius:26px;">
</p><font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif"><%= @teacher.name %></font></td>
<td><p><font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif"><%= @biography %></font></p>
</td>
</tr>
</table>
<p style="text-align:center"><font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif"><a href="<%= %>"style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">SCHEDULE FIRST FREE LESSON NOW</a></font></p>
<br>
<p><font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif">Click the button to schedule your first free lesson with the instructor listed above! Or if you think you'd prefer a different instructor, <a style="color: #ffcc00;" href="<%= User.search_url %>">click here</a> to search our guitar and bass instructors to select a different one.</font></p>
<p><font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif">If you have any questions, don't hesitate to contact us by email at <a href="mailto:support@jamkazam.com" style="color: #ffcc00;">support@jamkazam.com</a>.</font></p>
<% end %>
<p>
<font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif">
Best Regards,<br/>
Team JamKazam
</font>
</p>

View File

@ -0,0 +1,72 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JamKazam</title>
<style>
p {
margin-bottom: 0px;
line-height: 140%;
}
a {
color: #ffcc00 !important;
}
</style>
</head>
<body bgcolor="#000000" style="margin-top:10px;font-family:Arial, Helvetica, sans-serif;">
<table bgcolor="#262626" width="650" align="center" cellpadding="0" cellspacing="0">
<tr>
<td><img src="https://www.jamkazam.com/assets/email/header.png" width="650" height="183" alt="JamKazam"></td>
</tr>
</table>
<table bgcolor="#262626" width="650" align="center" cellpadding="30" cellspacing="0">
<tr>
<td align="left"><h1 style="font-size:22px;font-weight:normal;margin-top:0px">
<font color="#F34E1C" face="Arial, Helvetica, sans-serif"><%= yield(:title) %></font></h1>
<font color="#AAAAAA" face="Arial, Helvetica, sans-serif">
<%= yield %>
</font>
</td>
</tr>
<% unless @suppress_user_has_account_footer == true %>
<tr>
<td>
<table bgcolor="#21474C" cellpadding="10" cellspacing="0">
<tr>
<td align="left">
<!-- CALL OUT BOX -->
</font></p>
<p style="margin-top:0px">
<font size="2" color="#7FACBA" face="Arial, Helvetica, sans-serif">This email was sent to you because
you have an account at <a style="color: #ffcc00;" href="https://www.jamkazam.com">JamKazam</a>.
</td>
</tr>
</table>
</td>
</tr>
<% end %>
</table>
<table align="center" width="650" cellpadding="10" bgcolor="#156572" cellspacing="0">
<tr>
<td align="center">
<font size="1" color="#ffffff" face="Arial, Helvetica, sans-serif">Copyright &copy; <%= Time.now.year %> JamKazam,
Inc. All rights reserved.</font>
</td>
</tr>
</table>
</body>
</html>

View File

@ -4,6 +4,8 @@ module JamRuby
observe JamRuby::InvitedUser
def after_create(invited_user)
return # due to abuse
if invited_user.sender.nil?
InvitedUserMailer.welcome_betauser(invited_user).deliver_now
else

View File

@ -790,13 +790,16 @@ module JamRuby
lesson_type = 'single paid lesson'
end
User.where(id: booking.student.id).where('first_lesson_booked_at is NULL').update_all(first_lesson_booked_at: Time.now)
body = "Student has requested a #{lesson_type}!\n\nUser: #{booking.student.name}, #{booking.student.email}\nUser Admin URL:#{booking.student.admin_url}\n\n"
body += "Lesson Session URL: #{lesson_session.admin_url}"
AdminMailer.ugly({to: APP_CONFIG.email_support_alias,
subject:"Student '#{booking.student.name}' requested #{lesson_type}!",
body: body}).deliver_now
rescue
rescue => e
puts "Unable to alert that student requested lesson"
puts e
end
end

View File

@ -165,6 +165,51 @@ module JamRuby
end
end
def self.match_teacher(user)
instruments = user.instruments
if !instruments
instruments = []
end
instrument_ids = instruments.map(&:id)
if instrument_ids.length == 1 && instrument_ids[0] == 'other'
instrument_ids << 'electric guitar'
end
teacher = Teacher
.joins(:user)
.joins(:teachers_instruments)
.where('users.phantom IS FALSE')
.where('teachers.ready_for_session_at IS NOT NULL')
.where('teachers_instruments.instrument_id in (?)', instrument_ids)
.order('RANDOM()').limit(1).first
if teacher.nil?
teacher = Teacher
.joins(:user)
.joins(:teachers_instruments)
.where('users.phantom IS FALSE')
.where('teachers.ready_for_session_at IS NOT NULL')
.where('teachers_instruments.instrument_id in (?)', ['electric guitar'])
.order('RANDOM()').limit(1).first
else
teacher
end
if teacher.nil?
teacher = Teacher
.joins(:user)
.where('teachers.ready_for_session_at IS NOT NULL')
.where('users.phantom IS FALSE')
.order('RANDOM()').limit(1).first
else
teacher
end
teacher
end
def self.randomize_order
self.connection.execute("update teachers set random_order = sub.row_number * random() * 1000 from (select id, row_number() over () from teachers) as sub ;")
end

View File

@ -297,6 +297,7 @@ module JamRuby
scope :geocoded_users, -> { where(User.arel_table[:last_jam_locidispid].not_eq(nil)) }
scope :musicians_geocoded, -> { musicians.geocoded_users }
scope :email_opt_in, -> { where(:subscribe_email => true) }
scope :came_through_amazon, -> { joins(:posa_cards).where('posa_cards.lesson_package_type_id in (?)', LessonPackageType::AMAZON_PACKAGES + LessonPackageType::LESSON_PACKAGE_TYPES)}
def after_save
if school_interest && !school_interest_was
@ -376,6 +377,7 @@ module JamRuby
def self.hourly_check
send_onboarding_surveys
send_take_lesson_poke
end
def self.send_onboarding_surveys
@ -385,6 +387,15 @@ module JamRuby
end
end
def self.send_take_lesson_poke
User.came_through_amazon.where('remind_take_lesson_times <= 2').where('first_lesson_booked_at is NULL')
.where('(remind_take_lesson_at is NULL AND users.created_at < ?) OR remind_take_lesson_at < ? ', 1.hours.ago, 2.days.ago).each do |user|
UserMailer.amazon_prompt_take_lesson(user, Teacher.match_teacher(user).user, user.remind_take_lesson_times).deliver_now
User.where(id: user.id).update_all(remind_take_lesson_at: Time.now, remind_take_lesson_times: user.remind_take_lesson_times + 1 )
end
end
def update_teacher_pct
if teacher
teacher.update_profile_pct
@ -392,11 +403,11 @@ module JamRuby
end
def is_waiting_onboarding
ONBOARDING_STATUS_UNASSIGNED
ONBOARDING_STATUS_UNASSIGNED == onboarding_status
end
def is_onboarding
ONBOARDING_STATUS_ASSIGNED || ONBOARDING_STATUS_ESCALATED || ONBOARDING_STATUS_EMAILED
ONBOARDING_STATUS_ASSIGNED == onboarding_status || ONBOARDING_STATUS_ESCALATED == onboarding_status || ONBOARDING_STATUS_EMAILED == onboarding_status
end
def user_progression_fields

View File

@ -919,6 +919,16 @@ FactoryGirl.define do
lesson_package_type { JamRuby::LessonPackageType.test_drive_4 }
is_lesson true
end
factory :amazon_test_drive_free_2 do
card_type JamRuby::PosaCard::JAM_CLASS_2
credits 2
lesson_package_type { JamRuby::LessonPackageType.amazon_test_drive_free_2 }
is_lesson true
is_test false
purchased true
preactivate true
end
end
factory :posa_card_type, class: 'JamRuby::PosaCardType' do

View File

@ -747,6 +747,8 @@ describe LessonSession do
lesson_session.touch
lesson_session.music_session.creator.should eql lesson_session.lesson_booking.user
lesson_session.lesson_booking.teacher.should eql teacher
lesson_session.student.first_lesson_booked_at.should_not be_nil
query = LessonSession.index(user)[:query]
query.length.should eq 1

View File

@ -12,6 +12,23 @@ describe Teacher do
let(:instrument1) { FactoryGirl.create(:instrument, :description => 'a great instrument')}
let(:instrument2) { FactoryGirl.create(:instrument, :description => 'an ok instrument')}
describe "match_teacher" do
it "works" do
Teacher.match_teacher(user).should be_nil
teacher = FactoryGirl.create(:teacher, ready_for_session_at: Time.now)
Teacher.match_teacher(user).should eql teacher
teacher2 = FactoryGirl.create(:teacher, ready_for_session_at: nil)
Teacher.match_teacher(user).should eql teacher
teacher.ready_for_session_at = nil
teacher.save!
teacher2.ready_for_session_at = Time.now
teacher2.save!
Teacher.match_teacher(user).should eql teacher2
end
end
describe "index" do
it "no params" do
teacher = FactoryGirl.create(:teacher, ready_for_session_at: Time.now)

View File

@ -1017,6 +1017,98 @@ describe User do
UserMailer.deliveries.count.should eql 0
end
end
describe "send_take_lesson_poke" do
let(:user) {FactoryGirl.create(:user)}
before(:each) {
UserMailer.deliveries.clear
}
after {
Timecop.return
}
it "works" do
teacher = FactoryGirl.create(:teacher, ready_for_session_at: Time.now)
User.send_take_lesson_poke
UserMailer.deliveries.count.should eql 0
UserMailer.deliveries.clear
user.touch
User.send_take_lesson_poke
UserMailer.deliveries.count.should eql 0
UserMailer.deliveries.clear
posa_card = FactoryGirl.create(:amazon_test_drive_free_2)
posa_card.claim(user)
User.send_take_lesson_poke
UserMailer.deliveries.count.should eql 0
UserMailer.deliveries.clear
Timecop.freeze(2.hours.from_now)
User.send_take_lesson_poke
UserMailer.deliveries.count.should eql 1
UserMailer.deliveries.clear
user.reload
user.remind_take_lesson_times.should eql 1
user.remind_take_lesson_at.should_not be_nil
# no shift in time, so, no new one
User.send_take_lesson_poke
UserMailer.deliveries.count.should eql 0
UserMailer.deliveries.clear
user.reload
user.remind_take_lesson_times.should eql 1
user.remind_take_lesson_at.should_not be_nil
# jump 3 days in future
Timecop.freeze(3.days.from_now)
User.send_take_lesson_poke
UserMailer.deliveries.count.should eql 1
UserMailer.deliveries.clear
user.reload
user.remind_take_lesson_times.should eql 2
user.remind_take_lesson_at.should_not be_nil
# no shift in time, so, no new one
User.send_take_lesson_poke
UserMailer.deliveries.count.should eql 0
UserMailer.deliveries.clear
user.reload
user.remind_take_lesson_times.should eql 2
user.remind_take_lesson_at.should_not be_nil
# jump 3 days in future
Timecop.freeze(3.days.from_now)
User.send_take_lesson_poke
UserMailer.deliveries.count.should eql 1
UserMailer.deliveries.clear
user.reload
user.remind_take_lesson_times.should eql 3
user.remind_take_lesson_at.should_not be_nil
# no shift in time, so, no new one
User.send_take_lesson_poke
UserMailer.deliveries.count.should eql 0
UserMailer.deliveries.clear
user.reload
user.remind_take_lesson_times.should eql 3
user.remind_take_lesson_at.should_not be_nil
# jump 3 days in future
Timecop.freeze(3.days.from_now)
User.send_take_lesson_poke
UserMailer.deliveries.count.should eql 0
UserMailer.deliveries.clear
user.reload
user.remind_take_lesson_times.should eql 3
user.remind_take_lesson_at.should_not be_nil
end
end
=begin
describe "update avatar" do

View File

@ -388,6 +388,26 @@ describe "RenderMailers", :slow => true do
it { @filename="welcome_betauser"; InvitedUserMailer.welcome_betauser(admin_invited_user).deliver_now }
end
describe "Poke Amazon Users" do
let(:teacher) { u = FactoryGirl.create(:teacher, biography: 'This is a really long biography for a really worthless human that does not deserve the first bit of trash. What? What was I saying. Oh yes, right. For years me takey-takey lessons and givey-givey lessons. But so what you say? beat it'); u.user }
let(:user) { FactoryGirl.create(:user) }
before(:each) do
UserMailer.deliveries.clear
end
after(:each) do
UserMailer.deliveries.length.should == 1
# NOTE! we take the second email, because the act of creating the InvitedUser model
# sends an email too, before our it {} block runs. This is because we have an InvitedUserObserver
mail = UserMailer.deliveries[0]
save_emails_to_disk(mail, @filename)
end
it {@filename="amazon_prompt_take_lesson_1"; UserMailer.amazon_prompt_take_lesson(user, teacher, 0).deliver_now}
it {@filename="amazon_prompt_take_lesson_2"; UserMailer.amazon_prompt_take_lesson(user, teacher, 1).deliver_now}
it {@filename="amazon_prompt_take_lesson_3"; UserMailer.amazon_prompt_take_lesson(user, teacher, 2).deliver_now}
end
describe "Daily Scheduled Session emails" do
let (:scheduled_batch) { FactoryGirl.create(:email_batch_scheduled_session) }
let(:music_session) { FactoryGirl.create(:music_session) }

View File

@ -7,10 +7,10 @@
= image_tag("content/amazon_logo.jpg")
.success-msg
= "You're all set!"
= "Account successfully created!"
.instructions
p Within the next day or two, someone from JamKazam will reach out to you via email to help guide you through getting ready for your first lesson and picking a great teacher for you.
p You'll receive an email shortly from JamKazam with instructions on how to schedule your first free lesson.
p
| If you have any questions at any time, feel free to contact us at&nbsp;
a href="mailto:support@jamkazam.com" support@jamkazam

View File

@ -56,8 +56,9 @@ describe "Activate Account Card", :js => true, :type => :feature, :capybara_feat
find('a.amazon-a-button-text', text: 'Submit Code').trigger(:click)
find('.success-msg wbr', 'Your code has been validated!')
find('a.amazon-a-button-text', text: 'Create Account').trigger(:click)
select 'Acoustic Guitar', from: "instrument"
find('a.amazon-a-button-text', text: 'Create Account').trigger(:click)
find('.error', text: "Email can't be blank")
fill_in "email", with: "amzpos2@jamkazam.com"