This commit is contained in:
Seth Call 2014-07-31 20:16:38 -05:00
parent 1f19c9a4c3
commit 00dee99e17
11 changed files with 242 additions and 72 deletions

View File

@ -3,35 +3,90 @@
<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>The following new sessions have been posted within the last 24 hours, and you have good or acceptable latency to the organizer of each session below. If a session looks interesting, click the Details link to see the session page. You can RSVP to a session from the session page, and you'll be notified if/when the session organizer approves your RSVP.</p>
<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>
<style>
#scheduled-sessions {
margin-top:15px;
width:98%;
font-size:11px;
color:#fff;
background-color:#262626;
border-color:#cccccc;
border-width:1px 0;
border-style:solid;
}
<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">
#scheduled-sessions td {
padding:10px 2px;
}
#scheduled-sessions th {
padding:2px;
}
#scheduled-sessions thead {
margin-bottom: 5px;
}
#scheduled-sessions thead th {
border-width:0 0 1px 0;
border-style:solid;
border-color:#cccccc;
}
#scheduled-sessions tbody td {
border-width:1px 0 0 0;
border-style:solid;
border-color:#4d4d4d;
vertical-align:top;
}
#scheduled-sessions span.latency img {
margin-top:2px;
}
</style>
<table id="scheduled-sessions" cellspacing="0" cellpadding="0" border="0">
<!-- header -->
<thead>
<tr>
<th align="left" width="20%">GENRE</th>
<th align="left" width="60%">DESCRIPTION</th>
<th align="left" width="15%">GENRE</th>
<th align="left" width="20%">NAME</th>
<th align="left" width="45%">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 %></td>
</tr>
<% end %>
</thead>
<tbody>
<!-- session row goes here -->
<% @sessions_and_latency.each do |sess| %>
<tr>
<td><%= sess.genre.description %></td>
<td>
<%= sess.name %><br/>
<a href="<%= "http://www.jamkazam.com/sessions/#{sess.id}/details" %>">Details</a>
</td>
<td><%= sess.description %></td>
<td style="text-align:center">
<span class="latency">
<span class="latency-value"><%= sess.latency %> ms</span>
<% if sess.latency <= APP_CONFIG.max_good_full_score %>
<%= image_tag("http://www.jamkazam.com/assets/content/icon_green_score.png", alt: 'good score icon') %>
<% else %>
<%= image_tag("http://www.jamkazam.com/assets/content/icon_yellow_score.png", alt: 'fair score icon') %>
<% end %>
</span>
</td>
</tr>
<% end %>
</tbody>
</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>To see ALL the scheduled sessions that you might be interested in joining, view our <a href="http://www.jamkazam.com/client#/findSession">Find Session page</a>.</p>
<p>Best Regards,</p>

View File

@ -2,16 +2,11 @@
Hello <%= @user.first_name %> --
The following new sessions that that have been posted during the last 24 hours:
The following new sessions have been posted within the last 24 hours, and you have good or acceptable latency to the organizer of each session below. If a session looks interesting, click the Details link to see the session page. You can RSVP to a session from the session page, and you'll be notified if/when the session organizer approves your RSVP.
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
GENRE | NAME | DESCRIPTION | LATENCY
<% @sessions_and_latency.each do |sess| %>
<%= sess.genre.description %> | <%= sess.description %> | <%= sess.latency %>
<%= sess.genre.description %> | <%= sess.name %> | <%= sess.description %> | <%= sess.latency %> ms
<% 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.

View File

@ -9,7 +9,6 @@
margin-bottom:0px;
line-height:140%;
}
</style>
</head>

View File

@ -164,7 +164,7 @@ module JamRuby
# if user joins the session as a musician, update their addr and location
if as_musician
user.update_addr_loc(self, User::JAM_REASON_JOIN)
user.update_audio_latency(self, audio_latency)
user.update_audio_latency(self, audio_latency) if audio_latency # try not to let a previously recorded value get nil'ed
end
end

View File

@ -19,16 +19,43 @@ module JamRuby
def persisted?; false; end
end
# Temporary Tables created by this class:
# tmp_candidate_sessions
# ----------------------
#
# These are 'open' sessions that have any open slots left, and fall within a certain start time.
# The session creator must also have a locidispid.
#
# session_id - music_session.id
# creator_id - music_session.user_id
# creator_score_idx - this is the creator's users.last_jam_locidispid
# instrument_id - instruments that are open as gleamed from the RSVP. If this is NULL, it means 'ANY INSTRUMENT'
#
# tmp_candidate_recipients
# ------------------------
#
# These are musicians, that allow email notifications, that have an instrument which matches the session's open RSVP slot's instrument.
# The musician must also have a locidispid.
#
# receiver_id - user ID that could be in the session
# receiver_score_idx - the user's last_jam_locidispid
# instrument_id - the user's matching instrument for a open session slot. If this is NULL, it means 'ANY INSTRUMENT'
#
# tmp_matches
# -----------
#
# These are 'candidate_recipients' that have a decent enough score with the creator of the music sessions in tmp_candidate_sessions
#
# receiver_id - the user.id that should receive an Daily Session email
# session_id - the music_session.id for the email
# latency - the score.score between the creator and the candidate (needs to be full score soon)
class EmailBatchScheduledSessions < EmailBatchPeriodic
BATCH_SIZE = 500
SINCE_DAYS = 2
MIN_HOURS_START = 2
TMP_SESS = 'tmp_candidate_sessions'
TMP_RECIP = 'tmp_candidate_recipients'
TMP_MATCH = 'tmp_matches'
ENV_MAX_LATENCY = 'env_max_latency'
ENV_QUERY_LIMIT = 'env_query_limit'
SNAPSHOT_QUERY_LIMIT = '500'
@ -57,18 +84,18 @@ module JamRuby
end
def snapshot_eligible_sessions
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM #{TMP_SESS}")
[0 < rr.count ? rr[0]['num'].to_i : 0, ResultStub.stubs("SELECT * FROM #{TMP_SESS}")]
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM tmp_candidate_sessions")
[0 < rr.count ? rr[0]['num'].to_i : 0, ResultStub.stubs("SELECT * FROM tmp_candidate_sessions")]
end
def snapshot_eligible_recipients
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM #{TMP_RECIP}")
[0 < rr.count ? rr[0]['num'].to_i : 0, ResultStub.stubs("SELECT * FROM #{TMP_RECIP}")]
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM tmp_candidate_recipients")
[0 < rr.count ? rr[0]['num'].to_i : 0, ResultStub.stubs("SELECT * FROM tmp_candidate_recipients")]
end
def snapshot_scored_recipients
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM #{TMP_MATCH}")
[0 < rr.count ? rr[0]['num'].to_i : 0, ResultStub.stubs("SELECT * FROM #{TMP_MATCH}")]
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM tmp_matches")
[0 < rr.count ? rr[0]['num'].to_i : 0, ResultStub.stubs("SELECT * FROM tmp_matches")]
end
def take_snapshot
@ -91,9 +118,9 @@ module JamRuby
# now just get the sessions/latency for each distinct mail recipient
_select_scored_recipients(offset).each do |result|
receiver = User.find_by_id(result['receiver_id'])
sessions = MusicSession.select("music_sessions.*, #{TMP_MATCH}.latency")
.joins("INNER JOIN #{TMP_MATCH} ON #{TMP_MATCH}.session_id = music_sessions.id")
.where(["#{TMP_MATCH}.receiver_id = ?", receiver.id])
sessions = MusicSession.select("music_sessions.*, tmp_matches.latency")
.joins("INNER JOIN tmp_matches ON tmp_matches.session_id = music_sessions.id")
.where(["tmp_matches.receiver_id = ?", receiver.id])
.includes([:genre, :creator])
block_given? ? yield(receiver, sessions) : objs << [receiver, sessions]
end
@ -126,7 +153,7 @@ module JamRuby
# inserts eligible sessions to temp table
def _collect_eligible_sessions
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS #{TMP_SESS}")
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS tmp_candidate_sessions")
limit_sql = (self.snapshot? && 0 < ENV[ENV_QUERY_LIMIT].to_i) ? "LIMIT #{ENV[ENV_QUERY_LIMIT]}" : ''
sql =<<SQL
SELECT
@ -134,7 +161,7 @@ SELECT
msess.user_id AS creator_id,
users.last_jam_locidispid AS creator_score_idx,
rs.instrument_id
INTO TEMP TABLE #{TMP_SESS}
INTO TEMP TABLE tmp_candidate_sessions
FROM music_sessions msess
INNER JOIN users ON users.id = msess.user_id
INNER JOIN rsvp_slots AS rs ON rs.music_session_id = msess.id
@ -145,14 +172,14 @@ WHERE
msess.created_at > '#{earliest_session_create_time}' AND
msess.created_at < '#{latest_session_create_time}' AND
scheduled_start >= '#{earliest_session_start_time}' AND
(rrrs.rsvp_slot_id IS NULL OR rrrs.chosen != 't')
(rrrs.rsvp_slot_id IS NULL OR rrrs.chosen != TRUE)
#{limit_sql}
SQL
ActiveRecord::Base.connection.execute(sql)
end
def _collect_eligible_recipients
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS #{TMP_RECIP}")
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS tmp_candidate_recipients")
limit_sql = (self.snapshot? && 0 < ENV[ENV_QUERY_LIMIT].to_i) ? "LIMIT #{ENV[ENV_QUERY_LIMIT]}" : ''
# load eligible recipients into tmp table
sql =<<SQL
@ -160,40 +187,40 @@ SELECT
users.id AS receiver_id,
users.last_jam_locidispid AS receiver_score_idx,
mi.instrument_id
INTO TEMP TABLE #{TMP_RECIP}
INTO TEMP TABLE tmp_candidate_recipients
FROM users
INNER JOIN musicians_instruments AS mi ON mi.user_id = users.id
INNER JOIN #{TMP_SESS} ON #{TMP_SESS}.instrument_id = mi.instrument_id
LEFT JOIN tmp_candidate_sessions ON tmp_candidate_sessions.instrument_id = mi.instrument_id
WHERE
users.last_jam_locidispid IS NOT NULL AND
users.musician = 't' AND
users.subscribe_email = 't'
users.musician = TRUE AND
users.subscribe_email = TRUE
#{limit_sql}
SQL
ActiveRecord::Base.connection.execute(sql)
end
def _collect_scored_recipients
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS #{TMP_MATCH}")
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS tmp_matches")
if !self.snapshot? || 0 == (max_score = ENV[ENV_MAX_LATENCY].to_i)
max_score = Score::MAX_YELLOW_LATENCY
max_score = APP_CONFIG.max_yellow_full_score * 2
end
limit_sql = (self.snapshot? && 0 < ENV[ENV_QUERY_LIMIT].to_i) ? "LIMIT #{ENV[ENV_QUERY_LIMIT]}" : ''
sql =<<SQL
SELECT
DISTINCT #{TMP_RECIP}.receiver_id,
#{TMP_SESS}.session_id,
DISTINCT tmp_candidate_recipients.receiver_id,
tmp_candidate_sessions.session_id,
scores.score AS latency
INTO TEMP TABLE #{TMP_MATCH}
FROM scores
INNER JOIN #{TMP_SESS} ON #{TMP_SESS}.creator_score_idx = scores.alocidispid
INNER JOIN #{TMP_RECIP} ON #{TMP_RECIP}.receiver_score_idx = scores.blocidispid
INTO TEMP TABLE tmp_matches
FROM current_scores
INNER JOIN tmp_candidate_sessions ON tmp_candidate_sessions.creator_score_idx = current_scores.alocidispid
INNER JOIN tmp_candidate_recipients ON tmp_candidate_recipients.receiver_score_idx = current_scores.blocidispid
WHERE
scores.score < #{max_score} AND
#{TMP_RECIP}.receiver_id != #{TMP_SESS}.creator_id
current_scores.full_score < #{max_score} AND
tmp_candidate_recipients.receiver_id != tmp_candidate_sessions.creator_id
GROUP BY
#{TMP_RECIP}.receiver_id,
#{TMP_SESS}.session_id,
tmp_candidate_recipients.receiver_id,
tmp_candidate_sessions.session_id,
latency
#{limit_sql}
SQL
@ -203,13 +230,13 @@ SQL
# select recipients whose score is below minimum threshold
def _select_scored_recipients(offset=0)
if 0 > offset
sql = "SELECT COUNT(DISTINCT receiver_id) AS num FROM #{TMP_MATCH}"
sql = "SELECT COUNT(DISTINCT receiver_id) AS num FROM tmp_matches"
rr = ActiveRecord::Base.connection.execute(sql)
return 0 < rr.count ? rr[0]['num'].to_i : 0
else
sql =<<SQL
SELECT DISTINCT receiver_id
FROM #{TMP_MATCH}
FROM tmp_matches
ORDER BY receiver_id ASC
LIMIT #{@per_page}
OFFSET #{offset}
@ -233,11 +260,11 @@ SQL
return @counters if @counters || !self.snapshot?
_load_recipients if load_tmp_tables
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM #{TMP_SESS}")
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM tmp_candidate_sessions")
session_count = 0 < rr.count ? rr[0]['num'].to_i : 0
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM #{TMP_RECIP}")
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM tmp_candidate_recipients")
receiver_candidate_count = 0 < rr.count ? rr[0]['num'].to_i : 0
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM #{TMP_MATCH}")
rr = ActiveRecord::Base.connection.execute("SELECT COUNT(*) AS num FROM tmp_matches")
receiver_match_count = 0 < rr.count ? rr[0]['num'].to_i : 0
@counters = {

View File

@ -3,7 +3,7 @@ require 'ipaddr'
module JamRuby
class Score < ActiveRecord::Base
MAX_YELLOW_LATENCY = 40
MAX_YELLOW_LATENCY = 40 # this round-trip internet latency, not full score
self.table_name = 'scores'

View File

@ -63,6 +63,70 @@ describe "RenderMailers", :slow => true do
it { @filename="welcome_betauser"; InvitedUserMailer.welcome_betauser(admin_invited_user).deliver }
end
describe "Daily Scheduled Session emails" do
let (:scheduled_batch) { FactoryGirl.create(:email_batch_scheduled_session) }
let(:music_session) { FactoryGirl.create(:music_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 (:session1) do
FactoryGirl.create(:music_session,
:creator => drummer,
:scheduled_start => Time.now() + 2.days,
:musician_access => true,
:approval_required => false,
:created_at => Time.now - 1.hour)
end
let (:session2) do
FactoryGirl.create(:music_session,
:creator => drummer,
:scheduled_start => Time.now() + 2.days,
:musician_access => true,
:approval_required => false,
:created_at => Time.now - 1.hour)
end
before(:each) do
BatchMailer.deliveries.clear
scheduled_batch.reset!
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)
vocalist.musician_instruments << FactoryGirl.build(:musician_instrument, user: vocalist, instrument: vocals, 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)
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
after(:each) do
BatchMailer.deliveries.length.should == 1
mail = BatchMailer.deliveries[0]
save_emails_to_disk(mail, @filename)
end
it "daily sessions" do @filename="daily_sessions"; scheduled_batch.deliver_batch end
end
end
def save_emails_to_disk(mail, filename)

View File

@ -109,6 +109,19 @@ def app_config
def max_track_part_upload_failures
3
end
def max_good_full_score
20
end
def max_yellow_full_score
35
end
def max_red_full_score
50
end
private
def audiomixer_workspace_path

View File

@ -201,13 +201,13 @@
}
function renderNotConnected() {
console.log("RENDER NOT CONNECTED!!!!!!!!!")
logger.debug("text-message dialog: render not connected")
$interactionBlocker.addClass('active');
$disconnectedMsg.addClass('active');
}
function renderConnected() {
console.log("RENDER CONNECTED!!!!!!!!!")
logger.debug("text-message dialog: render connected")
$interactionBlocker.removeClass('active');
$disconnectedMsg.removeClass('active');
}

View File

@ -249,5 +249,10 @@ if defined?(Bundler)
# recording upload/download configs
config.max_track_upload_failures = 10
config.max_track_part_upload_failures = 3
# scoring thresholds for 'full score', which is 1/2 your gear + 1/2 their gear + and 1/2 ping time
config.max_good_full_score = 20
config.max_yellow_full_score = 35
config.max_red_full_score = 50
end
end

View File

@ -58,6 +58,18 @@ def web_config
def icecast_hardcoded_source_password
'blueberryjam'
end
def max_good_full_score
20
end
def max_yellow_full_score
35
end
def max_red_full_score
50
end
end
klass.new
end