Merge branch 'feature/scheduled_sessions' of bitbucket.org:jamkazam/jam-cloud into feature/scheduled_sessions

This commit is contained in:
Brian Smith 2014-05-31 07:35:07 -04:00
commit 5b438ffaef
12 changed files with 354 additions and 1 deletions

View File

@ -43,6 +43,7 @@ require "jam_ruby/resque/scheduled/icecast_source_check"
require "jam_ruby/resque/scheduled/cleanup_facebook_signup"
require "jam_ruby/resque/scheduled/unused_music_notation_cleaner"
require "jam_ruby/resque/scheduled/user_progress_emailer"
require "jam_ruby/resque/scheduled/daily_session_emailer"
require "jam_ruby/resque/google_analytics_event"
require "jam_ruby/mq_router"
require "jam_ruby/base_manager"
@ -149,6 +150,7 @@ 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_scheduled_sessions"
require "jam_ruby/models/email_batch_set"
require "jam_ruby/models/email_error"
require "jam_ruby/app/mailers/async_mailer"

View File

@ -391,6 +391,24 @@
end
end
def scheduled_session_daily(receiver, sessions_and_latency)
sendgrid_category "Notification"
sendgrid_unique_args :type => "scheduled_session_daily"
sendgrid_recipients([receiver.email])
sendgrid_substitute('@USERID', [receiver.id])
@user = receiver
@sessions_and_latency = sessions_and_latency
@title = 'New Scheduled Sessions Matched to You'
mail(:to => receiver.email,
:subject => EmailBatchScheduledSessions.subject) do |format|
format.text
format.html
end
end
def band_session_join(email, msg, session_id)
subject = "A band that you follow has joined a session"
unique_args = {:type => "band_session_join"}

View File

@ -0,0 +1,38 @@
<% provide(:title, @title) %>
<p>Hello <%= @user.first_name %> --
</p>
<p>The following new sessions that that have been posted during the last 24 hours:
</p>
<ol>
<li>Need someone who plays an instrument that you play</li>
<li>Were posted by someone to whom you have either a good or medium latency connection</li>
</ol>
<p>Take a look through these new sessions below, and just click the RSVP button on the far right side of the row for any session in which you'd like to play. This will let the session organizer know you're interested, and you'll be notified if the session organizer accepts your request to play in that session!
</p>
<table style="margin-top:6px; width:98%; font-size:11px; color:#fff; background-color:#262626; border:solid 1px #4d4d4d;" cellspacing="0" cellpadding="0" border="0">
<!-- header -->
<tr>
<th align="left" width="20%">GENRE</th>
<th align="left" width="60%">DESCRIPTION</th>
<th width="20%" style="text-align:center">LATENCY</th>
</tr>
<!-- session row goes here -->
<% @sessions_and_latency.each do |sess| %>
<tr>
<td><%= sess.genre.description %></td>
<td><%= sess.description %></td>
<td style="text-align:center"><%= sess.latency_store %></td>
</tr>
<% end %>
</table>
<p>To see ALL the scheduled sessions that you might be interested in joining, view our Find Session page at: <a href="http://www.jamkazam.com/client#/findSession">http://www.jamkazam.com/client#/findSession</a>.
</p>
<p>Best Regards,</p>
Team JamKazam

View File

@ -0,0 +1,21 @@
<% provide(:title, @title) %>
Hello <%= @user.first_name %> --
The following new sessions that that have been posted during the last 24 hours:
1. Need someone who plays an instrument that you play
2. Were posted by someone to whom you have either a good or medium latency connection
Take a look through these new sessions below, and just click the RSVP button on the far right side of the row for any session in which you'd like to play. This will let the session organizer know you're interested, and you'll be notified if the session organizer accepts your request to play in that session!
GENRE | DESCRIPTION | LATENCY
<% @sessions_and_latency.each do |sess| %>
<%= sess.genre.description %> | <%= sess.description %> | <%= sess.latency_store %>
<% end %>
To see ALL the scheduled sessions that you might be interested in joining, view our Find Session page at: http://www.jamkazam.com/client#/findSession.
Best Regards,
Team JamKazam

View File

@ -0,0 +1,129 @@
module JamRuby
class EmailBatchScheduledSessions < EmailBatchPeriodic
BATCH_SIZE = 500
SINCE_DAYS = 2
MIN_HOURS_START = 2
TMP_SNAP = 'scheduled_session_snapshot'
TMP_USER = 'scheduled_session_user'
def self.subject
"New sessions have been scheduled that may be a good match for you!"
end
# def fetch_sessions
# objs = []
# MusicSession.open_sessions
# .where(['created_at > ?', time_since_last_batch(SINCE_DAYS)])
# .where(['scheduled_start >= ?', Time.now() + MIN_HOURS_START.hours])
# .find_each do |ss|
# block_given? ? yield(ss) : objs << ss
# end
# objs
# end
# inserts eligible sessions to temp table
def prep_tmp_table
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS #{TMP_SNAP}")
sql =<<SQL
SELECT
rs.instrument_id AS instrument_id,
msess.id AS session_id,
msess.user_id AS creator_id,
users.last_jam_locidispid AS creator_score_idx
INTO TEMP TABLE #{TMP_SNAP}
FROM music_sessions msess
INNER JOIN rsvp_slots AS rs ON rs.music_session_id = msess.id
LEFT JOIN rsvp_requests_rsvp_slots AS rrrs ON rrrs.rsvp_slot_id = rs.id
INNER JOIN users ON users.id = msess.user_id
WHERE
musician_access = 't' AND
approval_required = 'f' AND
msess.created_at > '#{time_since_last_batch(SINCE_DAYS)}' AND
scheduled_start >= '#{Time.now() + MIN_HOURS_START.hours}' AND
(rrrs.rsvp_slot_id IS NULL OR rrrs.chosen != 't')
SQL
ActiveRecord::Base.connection.execute(sql)
end
def fetch_recipients
objs = []
# load eligible sessions into tmp table
self.prep_tmp_table
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS #{TMP_USER}")
# load eligible recipients into tmp table
sql =<<SQL
SELECT
users.id AS user_id,
users.last_jam_locidispid AS user_score_idx,
tmp.session_id AS session_id,
tmp.creator_id AS creator_id,
tmp.creator_score_idx AS creator_score_idx
INTO TEMP TABLE #{TMP_USER}
FROM users
INNER JOIN musicians_instruments AS mi ON mi.user_id = users.id
LEFT JOIN #{TMP_SNAP} AS tmp ON tmp.instrument_id = mi.instrument_id
WHERE
users.musician = 't' AND
users.subscribe_email = 't' AND
tmp.session_id IS NOT NULL
GROUP BY users.id, tmp.session_id, tmp.creator_id, tmp.creator_score_idx
SQL
ActiveRecord::Base.connection.execute(sql)
# select recipients whose score is below minimum threshold
sql =<<SQL
SELECT DISTINCT user_id, scores.score AS latency
FROM #{TMP_USER}
INNER JOIN scores ON scores.alocidispid = #{TMP_USER}.creator_score_idx AND scores.blocidispid = #{TMP_USER}.user_score_idx
WHERE
scores.score < #{Score::MAX_YELLOW_LATENCY}
SQL
results = ActiveRecord::Base.connection.execute(sql)
# now just get the sessions/latency for each distinct mail recipient
results.each do |result|
sql =<<SQL
SELECT session_id
FROM #{TMP_USER}
WHERE
user_id = '#{result['user_id']}'
SQL
user = User.find_by_id(result['user_id'])
sessions = ActiveRecord::Base.connection.execute(sql).collect do |rr|
msess = MusicSession.where(['id = ?',rr['session_id']])
.limit(1)
.includes([:genre, :creator])
.first
msess.latency_store = result['latency']
msess
end
block_given? ? yield(user, sessions) : objs << [user, sessions]
end
objs
end
def deliver_batch_sets!
self.opt_in_count = 0
sent = 0
self.fetch_recipients do |receiver, sessions_and_latency|
self.opt_in_count += 1
sent += 1
bset = EmailBatchSet.scheduled_session_set(self, receiver, sessions_and_latency)
UserMailer.scheduled_session_daily(receiver, sessions_and_latency).deliver
end
self.sent_count = sent
self.save
self.did_batch_run!
end
def self.send_daily_session_batch
oo = self.create
oo..deliver_batch
oo
end
end
end

View File

@ -27,6 +27,17 @@ module JamRuby
bset
end
def self.scheduled_session_set(batch, receiver, sessions)
bset = self.new
bset.email_batch = batch
bset.user = receiver
bset.user_ids = sessions.map(&:id).join(',')
bset.started_at = Time.now
bset.batch_count = 1
bset.save!
bset
end
def subject
unless sub_type.blank?
return EmailBatchProgression.subject(self.sub_type.to_sym)

View File

@ -10,6 +10,8 @@ module JamRuby
attr_accessor :legal_terms, :recurring_mode, :language_description, :scheduled_start_time, :access_description
attr_accessor :latency_store
self.table_name = "music_sessions"
self.primary_key = 'id'

View File

@ -3,6 +3,8 @@ require 'ipaddr'
module JamRuby
class Score < ActiveRecord::Base
MAX_YELLOW_LATENCY = 40
self.table_name = 'scores'
attr_accessible :alocidispid, :anodeid, :aaddr, :blocidispid, :bnodeid, :baddr, :score, :score_dt, :scorer, :scoring_data
@ -31,5 +33,6 @@ module JamRuby
def self.score_conns(c1, c2, score)
self.createx(c1.locidispid, c1.client_id, c1.addr, c2.locidispid, c2.client_id, c2.addr, score)
end
end
end

View File

@ -0,0 +1,15 @@
module JamRuby
class DailySessionEmailer
extend Resque::Plugins::LonelyJob
@queue = :scheduled_daily_session_emailer
@@log = Logging.logger[DailySessionEmailer]
def self.perform
@@log.debug("waking up")
EmailBatchScheduledSessions.send_daily_session_batch
@@log.debug("done")
end
end
end

View File

@ -457,6 +457,9 @@ FactoryGirl.define do
factory :email_batch_progression, :class => JamRuby::EmailBatchProgression do
end
factory :email_batch_scheduled_session, :class => JamRuby::EmailBatchScheduledSessions do
end
factory :notification, :class => JamRuby::Notification do
factory :notification_text_message do
@ -474,7 +477,7 @@ FactoryGirl.define do
factory :rsvp_slot, class: JamRuby::RsvpSlot do
association :instrument, factory: :instrument
association :music_session, factory: :music_session
association :rsvp_request_slot, factory: :rsvp_request_slot
# association :rsvp_request_slot, factory: :rsvp_request_slot
proficiency_level 'beginner'
end

View File

@ -0,0 +1,106 @@
require 'spec_helper'
describe EmailBatch do
after(:each) do
Timecop.return
end
before(:each) do
UserMailer.deliveries.clear
end
describe 'daily scheduled' do
# before { pending }
let (:scheduled_batch) { FactoryGirl.create(:email_batch_scheduled_session) }
let (:drums) { FactoryGirl.create(:instrument, :description => 'drums') }
let (:guitar) { FactoryGirl.create(:instrument, :description => 'guitar') }
let (:bass) { FactoryGirl.create(:instrument, :description => 'bass') }
let (:vocals) { FactoryGirl.create(:instrument, :description => 'vocal') }
let (:drummer) { FactoryGirl.create(:user,
:last_jam_locidispid => 1,
:last_jam_addr => 1) }
let (:guitarist) { FactoryGirl.create(:user,
:last_jam_locidispid => 1,
:last_jam_addr => 1) }
let (:bassist) { FactoryGirl.create(:user,
:last_jam_locidispid => 1,
:last_jam_addr => 1) }
let (:vocalist) { FactoryGirl.create(:user,
:last_jam_locidispid => 1,
:last_jam_addr => 1) }
let (:loser) { FactoryGirl.create(:user,
:last_jam_locidispid => 2,
:last_jam_addr => 2) }
let (:session1) do
FactoryGirl.create(:music_session,
:creator => drummer,
:scheduled_start => Time.now() + 2.days,
:musician_access => true,
:approval_required => false)
end
let (:session2) do
FactoryGirl.create(:music_session,
:creator => drummer,
:scheduled_start => Time.now() + 2.days,
:musician_access => true,
:approval_required => false)
end
before(:each) do
MusicianInstrument.delete_all
RsvpSlot.delete_all
JamRuby::Score.delete_all
drummer.musician_instruments << FactoryGirl.build(:musician_instrument, user: drummer, instrument: drums, proficiency_level: 2)
drummer.musician_instruments << FactoryGirl.build(:musician_instrument, user: drummer, instrument: guitar, proficiency_level: 2)
guitarist.musician_instruments << FactoryGirl.build(:musician_instrument, user: guitarist, instrument: guitar, proficiency_level: 2)
guitarist.musician_instruments << FactoryGirl.build(:musician_instrument, user: guitarist, instrument: bass, proficiency_level: 2)
bassist.musician_instruments << FactoryGirl.build(:musician_instrument, user: bassist, instrument: bass, proficiency_level: 2)
bassist.musician_instruments << FactoryGirl.build(:musician_instrument, user: bassist, instrument: guitar, proficiency_level: 2)
vocalist.musician_instruments << FactoryGirl.build(:musician_instrument, user: vocalist, instrument: vocals, proficiency_level: 2)
loser.musician_instruments << FactoryGirl.build(:musician_instrument, user: loser, instrument: vocals, proficiency_level: 2)
loser.musician_instruments << FactoryGirl.build(:musician_instrument, user: loser, instrument: drums, proficiency_level: 2)
FactoryGirl.create(:rsvp_slot, :instrument => drums, :music_session => session1)
FactoryGirl.create(:rsvp_slot, :instrument => guitar, :music_session => session1)
FactoryGirl.create(:rsvp_slot, :instrument => bass, :music_session => session1)
FactoryGirl.create(:rsvp_slot, :instrument => drums, :music_session => session2)
FactoryGirl.create(:rsvp_slot, :instrument => guitar, :music_session => session2)
FactoryGirl.create(:rsvp_slot, :instrument => bass, :music_session => session2)
# oo = FactoryGirl.create(:rsvp_slot, :instrument => vocals, :music_session => session1)
# oo.rsvp_request_slot.update_attributes(chosen: true)
# oo = FactoryGirl.create(:rsvp_request, :user => vocalist, :rsvp_slot => oo)
# oo.rsvp_request_slot.update_attributes(chosen: true)
JamRuby::Score.createx(1, 'a', 1, 1, 'a', 1, 10)
JamRuby::Score.createx(1, 'a', 1, 2, 'a', 2, Score::MAX_YELLOW_LATENCY + 1)
end
before(:each) do
end
it 'sets up data properly' do
expect(drummer.instruments.include?(drums)).to eq(true)
expect(drummer.instruments.include?(guitar)).to eq(true)
obj = scheduled_batch.fetch_recipients
expect(obj.count).to eq(3)
end
it 'sends email' do
ebatch = scheduled_batch
ebatch.deliver_batch
expect(UserMailer.deliveries.length).to eq(3)
end
end
end

View File

@ -33,3 +33,8 @@ UserProgressEmailer:
cron: "30 21 * * *"
class: "JamRuby::UserProgressEmailer"
description: "Sends periodic user progress emails"
DailySessionEmailer:
cron: "0 6 * * *"
class: "JamRuby::DailySessionEmailer"
description: "Sends daily scheduled session emails"