Merge branch 'feature/latency_hover' into develop

This commit is contained in:
Seth Call 2014-08-19 20:59:02 -05:00
commit 4a0919e1bc
56 changed files with 1081 additions and 480 deletions

View File

@ -201,4 +201,5 @@ current_scores_use_median.sql
current_scores_ams_index_sms_index_use_user_instrument.sql
locidispid_in_score_histories.sql
define_environment_in_db.sql
drop_session_invite_constraint.sql
drop_session_invite_constraint.sql
sms_index_single_session.sql

View File

@ -0,0 +1,157 @@
-- changelog:
-- * allow session_id to be passed to sms_index, which skips all the access questions, and just makes sure you'll get back that session, with user scores
-- * in both sms_index and ams_index, in the user tmp table, return also internet score and the other user's audio latency
-- check that the music_sessions does not currently have an active_music_sessions
CREATE OR REPLACE FUNCTION sms_index (my_user_id VARCHAR, my_locidispid BIGINT, my_audio_latency INTEGER, session_id VARCHAR) RETURNS VOID STRICT VOLATILE AS $$
BEGIN
-- output table to hold tagged music sessions with latency
CREATE TEMPORARY TABLE sms_music_session_tmp (music_session_id VARCHAR(64) NOT NULL, tag INTEGER, latency INTEGER) ON COMMIT DROP;
IF session_id = 'any' THEN
-- populate sms_music_session_tmp as all music sessions
-- XXX: we should pass in enough info to match pagination/query to reduce the impact of this step
INSERT INTO sms_music_session_tmp SELECT DISTINCT id, NULL::INTEGER AS tag, NULL::INTEGER AS latency
FROM music_sessions
WHERE (scheduled_start IS NULL OR scheduled_start > (NOW() - (interval '15 minute')))
AND canceled = FALSE
AND id NOT IN (SELECT id FROM active_music_sessions);
-- tag accepted rsvp as 1
UPDATE sms_music_session_tmp q SET tag = 1 FROM rsvp_slots s, rsvp_requests_rsvp_slots rrs, rsvp_requests r WHERE
q.music_session_id = s.music_session_id AND
s.id = rrs.rsvp_slot_id AND
rrs.rsvp_request_id = r.id AND
r.user_id = my_user_id AND
rrs.chosen = TRUE AND
q.tag is NULL;
-- tag invitation as 2
UPDATE sms_music_session_tmp q SET tag = 2 FROM invitations i WHERE
q.music_session_id = i.music_session_id AND
i.receiver_id = my_user_id AND
q.tag IS NULL;
-- musician access as 3
UPDATE sms_music_session_tmp q SET tag = 3 FROM music_sessions m WHERE
q.music_session_id = m.id AND
m.open_rsvps = TRUE AND
q.tag IS NULL;
-- delete anything not tagged
DELETE FROM sms_music_session_tmp WHERE tag IS NULL;
ELSE
INSERT INTO sms_music_session_tmp SELECT DISTINCT id, NULL::INTEGER AS tag, NULL::INTEGER AS latency
FROM music_sessions
WHERE music_sessions.id = session_id;
END IF;
-- output table to hold users involved in the sms_music_session_tmp sessions and their latency
CREATE TEMPORARY TABLE sms_users_tmp (music_session_id VARCHAR(64), user_id VARCHAR(64) NOT NULL, full_score INTEGER, audio_latency INTEGER, internet_score INTEGER) ON COMMIT DROP;
IF my_audio_latency > -1 THEN
-- populate sms_users_tmp with users that have an approved RSVP for sessions in the sms_music_session_tmp table, accompanied with full latency and music session
INSERT INTO sms_users_tmp SELECT q.music_session_id, users.id, s.full_score AS full_score, s.a_audio_latency, s.score
FROM sms_music_session_tmp q
INNER JOIN rsvp_slots ON rsvp_slots.music_session_id = q.music_session_id
INNER JOIN rsvp_requests_rsvp_slots ON rsvp_requests_rsvp_slots.rsvp_slot_id = rsvp_slots.id
INNER JOIN rsvp_requests ON rsvp_requests.id = rsvp_requests_rsvp_slots.rsvp_request_id
INNER JOIN users ON rsvp_requests.user_id = users.id
LEFT OUTER JOIN current_scores s ON s.alocidispid = users.last_jam_locidispid
WHERE
s.blocidispid = my_locidispid;
-- populate sms_users_tmp with invited users for session in the sms_music_session_tmp table, accompanied with full latency and music session
-- specify NULL for music_session_id, because we don't want RSVP users to affect the AVG computed for each session later
INSERT INTO sms_users_tmp SELECT NULL, users.id, s.full_score AS full_score, s.a_audio_latency, s.score
FROM sms_music_session_tmp q
INNER JOIN invitations ON invitations.music_session_id = q.music_session_id
INNER JOIN users ON invitations.receiver_id = users.id
LEFT OUTER JOIN current_scores s ON s.alocidispid = users.last_jam_locidispid
WHERE
s.blocidispid = my_locidispid AND
users.id NOT IN (SELECT user_id FROM sms_users_tmp);
END IF;
-- calculate the average latency
UPDATE sms_music_session_tmp q SET latency = (select AVG(u.full_score) FROM sms_users_tmp u WHERE
q.music_session_id = u.music_session_id);
RETURN;
END;
$$ LANGUAGE plpgsql;
-- my_audio_latency can have a special value of -1, which means 'unknown'.
CREATE OR REPLACE FUNCTION ams_index (my_user_id VARCHAR, my_locidispid BIGINT, my_audio_latency INTEGER) RETURNS VOID STRICT VOLATILE AS $$
BEGIN
-- output table to hold tagged music sessions with latency
CREATE TEMPORARY TABLE ams_music_session_tmp (music_session_id VARCHAR(64) NOT NULL, tag INTEGER, latency INTEGER) ON COMMIT DROP;
-- populate ams_music_session_tmp as all music sessions
INSERT INTO ams_music_session_tmp SELECT DISTINCT id, NULL::INTEGER AS tag, NULL::INTEGER AS latency
FROM active_music_sessions;
-- TODO worry about active music session where my_user_id is the creator?
-- eh, maybe, but if the music session is active and you're the creator wouldn't you already be in it?
-- so maybe you're on another computer, so why care? plus seth is talking about auto rsvp'ing the session
-- for you, so maybe not a problem.
-- tag accepted rsvp as 1
UPDATE ams_music_session_tmp q SET tag = 1 FROM rsvp_slots s, rsvp_requests_rsvp_slots rrs, rsvp_requests r WHERE
q.music_session_id = s.music_session_id AND
s.id = rrs.rsvp_slot_id AND
rrs.rsvp_request_id = r.id AND
r.user_id = my_user_id AND
rrs.chosen = TRUE AND
q.tag is NULL;
-- tag invitation as 2
UPDATE ams_music_session_tmp q SET tag = 2 FROM invitations i WHERE
q.music_session_id = i.music_session_id AND
i.receiver_id = my_user_id AND
q.tag IS NULL;
-- musician access as 3
UPDATE ams_music_session_tmp q SET tag = 3 FROM music_sessions m WHERE
q.music_session_id = m.id AND
m.musician_access = TRUE AND
q.tag IS NULL;
-- delete anything not tagged
DELETE FROM ams_music_session_tmp WHERE tag IS NULL;
-- output table to hold users involved in the ams_music_session_tmp sessions and their latency
CREATE TEMPORARY TABLE ams_users_tmp (music_session_id VARCHAR(64), user_id VARCHAR(64) NOT NULL, full_score INTEGER, audio_latency INTEGER, internet_score INTEGER) ON COMMIT DROP;
IF my_audio_latency > -1 THEN
-- populate ams_users_tmp with users that have a connection for sessions in the ams_music_session_tmp table, accompanied with full latency and music session
INSERT INTO ams_users_tmp SELECT c.music_session_id, c.user_id, s.full_score AS full_score, s.a_audio_latency, s.score
FROM ams_music_session_tmp q
INNER JOIN connections c ON c.music_session_id = q.music_session_id
LEFT OUTER JOIN current_scores s ON s.alocidispid = c.locidispid
WHERE s.blocidispid = my_locidispid;
-- populate ams_users_tmp with users that have an approved RSVP for sessions inthe ams_music_session_tmp table, accompanied with full latency and music session
-- specify NULL for music_session_id, because we don't want RSVP users to affect the AVG computed for each session later
INSERT INTO ams_users_tmp SELECT NULL, users.id, s.full_score AS full_score, s.a_audio_latency, s.score
FROM ams_music_session_tmp q
INNER JOIN rsvp_slots ON rsvp_slots.music_session_id = q.music_session_id
INNER JOIN rsvp_requests_rsvp_slots ON rsvp_requests_rsvp_slots.rsvp_slot_id = rsvp_slots.id
INNER JOIN rsvp_requests ON rsvp_requests.id = rsvp_requests_rsvp_slots.rsvp_request_id
INNER JOIN users ON rsvp_requests.user_id = users.id
LEFT OUTER JOIN current_scores s ON s.alocidispid = users.last_jam_locidispid
WHERE
s.blocidispid = my_locidispid AND
rsvp_requests_rsvp_slots.chosen = TRUE AND
users.id NOT IN (SELECT user_id FROM ams_users_tmp);
END IF;
-- calculate the average latency
UPDATE ams_music_session_tmp q SET latency = (select AVG(u.full_score) FROM ams_users_tmp u WHERE
q.music_session_id = u.music_session_id);
RETURN;
END;
$$ LANGUAGE plpgsql;

View File

@ -74,7 +74,7 @@
<td style="text-align:center">
<span class="latency">
<span class="latency-value"><%= (sess.latency / 2).round %> ms</span>
<% if sess.latency <= (APP_CONFIG.max_good_full_score * 2) %>
<% 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') %>

View File

@ -319,12 +319,9 @@ module JamRuby
# initialize the two temporary tables we use to drive ams_index and ams_users
def self.ams_init(current_user, options = {})
client_id = options[:client_id]
connection = Connection.where(user_id: current_user.id, client_id: client_id).first!
my_locidispid = connection.locidispid
my_locidispid = current_user.last_jam_locidispid
# 13 is an average audio gear value we use if they have not qualified any gear
my_audio_latency = connection.last_jam_audio_latency || current_user.last_jam_audio_latency || 13
my_audio_latency = current_user.last_jam_audio_latency || 13
locidispid_expr = my_locidispid ? "#{my_locidispid}::bigint" : '0::bigint'
self.connection.execute("select ams_index('#{current_user.id}'::varchar, #{locidispid_expr}, #{my_audio_latency}::integer)").check
@ -420,7 +417,7 @@ module JamRuby
# ams_init must be called first.
# user.audio_latency / 2 , + other_user.audio_latency of them / 2, + network latency /2
def self.ams_users
return User.select('users.*, ams_users_tmp.music_session_id, ams_users_tmp.latency')
return User.select('users.*, ams_users_tmp.music_session_id, ams_users_tmp.full_score, ams_users_tmp.audio_latency, ams_users_tmp.internet_score')
.joins(
%Q{
INNER JOIN
@ -444,7 +441,7 @@ module JamRuby
user_scores = {}
music_session_users.each do |user|
user_scores[user.id] = {latency: user.latency}
user_scores[user.id] = {full_score: user.full_score, audio_latency: user.audio_latency, internet_score: user.internet_score}
end
[music_sessions, user_scores]

View File

@ -6,6 +6,10 @@ module JamRuby
validates :env, :inclusion => {:in => ['development', 'staging', 'production', 'test']}
def self.env
GenericState.singleton.env
end
def self.allow_emails?
database_environment = singleton.env

View File

@ -51,7 +51,7 @@ module JamRuby
end
end
{city: city, state: state, country: country, addr: addr, locidispid: (locid.nil? || ispid.nil?) ? nil : locid*1000000+ispid}
{city: city, state: state, country: country, addr: addr, locidispid: (locid.nil? || ispid.nil?) ? nil : Score.compute_locidispid(locid, ispid) }
end
def self.createx(locid, countrycode, region, city, postalcode, latitude, longitude, metrocode, areacode)

View File

@ -493,7 +493,7 @@ module JamRuby
# retrieve users that have approved RSVPs
def approved_rsvps
User.find_by_sql(%Q{select distinct ON(u.id) u.id, u.photo_url, u.first_name, u.last_name, json_agg(ii.id) as instrument_ids, json_agg(ii.description) as instrument_descriptions, json_agg(rs.proficiency_level) as instrument_proficiencies, json_agg(rr.id) as rsvp_request_ids
User.find_by_sql(%Q{select distinct ON(u.id) u.id, u.photo_url, u.first_name, u.last_name, u.last_jam_audio_latency, json_agg(ii.id) as instrument_ids, json_agg(ii.description) as instrument_descriptions, json_agg(rs.proficiency_level) as instrument_proficiencies, json_agg(rr.id) as rsvp_request_ids
from rsvp_slots rs
inner join rsvp_requests_rsvp_slots rrrs on rrrs.rsvp_slot_id = rs.id
inner join rsvp_requests rr on rrrs.rsvp_request_id = rr.id
@ -590,15 +590,14 @@ module JamRuby
# initialize the two temporary tables we use to drive sms_index and sms_users
def self.sms_init(current_user, options = {})
client_id = options[:client_id]
session_id = options[:session_id] || 'any'
connection = Connection.where(user_id: current_user.id, client_id: client_id).first!
my_locidispid = connection.locidispid
my_locidispid = current_user.last_jam_locidispid
# 13 is an average audio gear value we use if they have not qualified any gear
my_audio_latency = connection.last_jam_audio_latency || current_user.last_jam_audio_latency || 13
my_audio_latency = current_user.last_jam_audio_latency || 13
locidispid_expr = my_locidispid ? "#{my_locidispid}::bigint" : '0::bigint' # Have to pass in zero; NULL fails silently in the stored proc
self.connection.execute("SELECT sms_index('#{current_user.id}'::varchar, #{locidispid_expr}, #{my_audio_latency}::integer)").check
self.connection.execute("SELECT sms_index('#{current_user.id}'::varchar, #{locidispid_expr}, #{my_audio_latency}::integer, #{ActiveRecord::Base.connection.quote(session_id)}::varchar)").check
end
# Generate a list of music sessions (that are active) filtered by genre, language, keyword, and sorted
@ -691,7 +690,7 @@ module JamRuby
# sms_init must be called first.
# user.audio_latency / 2 , + other_user.audio_latency of them / 2, + network latency /2
def self.sms_users
return User.select('users.*, sms_users_tmp.music_session_id, sms_users_tmp.latency')
return User.select('users.*, sms_users_tmp.music_session_id, sms_users_tmp.full_score, sms_users_tmp.audio_latency, sms_users_tmp.internet_score')
.joins(
%Q{
INNER JOIN
@ -715,12 +714,29 @@ module JamRuby
user_scores = {}
music_session_users.each do |user|
user_scores[user.id] = {latency: user.latency}
user_scores[user.id] = {full_score: user.full_score, audio_latency: user.audio_latency, internet_score: user.internet_score}
end
[music_sessions, user_scores]
end
# returns a single session, but populates any other user info with latency scores, so that show_history.rabl can do it's business
def self.session_with_scores(current_user, music_session_id)
MusicSession.sms_init(current_user, {session_id: music_session_id})
music_session = MusicSession.find(music_session_id)
music_session_users = MusicSession.sms_users.all
user_scores = {}
music_session_users.each do |user|
user_scores[user.id] = {full_score: user.full_score, audio_latency: user.audio_latency, internet_score: user.internet_score}
end
[music_session, user_scores]
end
def self.upcoming_sessions
end

View File

@ -225,8 +225,8 @@ module JamRuby
rel = rel.where(['current_scores.full_score > ?', score_min]) unless score_min.nil?
rel = rel.where(['current_scores.full_score <= ?', score_max]) unless score_max.nil?
rel = rel.select('current_scores.full_score, current_scores.score, current_scores.b_audio_latency as audio_latency, regions.regionname')
rel = rel.group('current_scores.full_score, current_scores.score, current_scores.b_audio_latency, regions.regionname')
rel = rel.select('current_scores.full_score, current_scores.score, regions.regionname')
rel = rel.group('current_scores.full_score, current_scores.score, regions.regionname')
end
ordering = self.order_param(params)

View File

@ -323,12 +323,26 @@ module JamRuby
read_attribute(:music_session_id)
end
def latency
return nil unless has_attribute?(:latency)
a = read_attribute(:latency)
# ===== ARTIFICIAL ATTRIBUTES CREATED BY ActiveMusicSession.ams_users, MusicSession.sms_uses
def full_score
return nil unless has_attribute?(:full_score)
a = read_attribute(:full_score)
a.nil? ? nil : a.to_i
end
def internet_score
return nil unless has_attribute?(:internet_score)
a = read_attribute(:internet_score)
a.nil? ? nil : a.to_i
end
def audio_latency
return nil unless has_attribute?(:audio_latency)
a = read_attribute(:audio_latency)
a.nil? ? nil : a.to_i
end
# ====== END ARTIFICAL ATTRIBUTES
# mods comes back as text; so give ourselves a parsed version
def mods_json
@mods_json ||= mods ? JSON.parse(mods, symbolize_names: true) : {}
@ -1242,15 +1256,16 @@ module JamRuby
end
def update_audio_latency(connection, audio_latency)
if audio_latency > 2
# updating the connection is best effort
if connection
connection.last_jam_audio_latency = audio_latency
connection.save
end
# updating the connection is best effort
if connection
connection.last_jam_audio_latency = audio_latency
connection.save
self.last_jam_audio_latency = audio_latency
self.save
end
self.last_jam_audio_latency = audio_latency
self.save
end
def top_followings

View File

@ -376,17 +376,25 @@ describe ActiveMusicSession do
users.length.should == 2
if users[0].music_session_id == earlier_session.id
users[0].id.should == creator.id
users[0].latency.should == 15 # (5 + 20 + 5) / 2
users[0].full_score.should == 30 # (5 + 20 + 5)
users[0].audio_latency = 5
users[0].internet_score = 20
users[1].music_session_id == later_session.id
users[1].id.should == creator2.id
users[1].latency.should == 22 # (5 + 30 + 10) / 2
users[1].full_score.should == 45 # (5 + 30 + 10)
users[1].audio_latency.should == 10
users[1].internet_score.should == 30
else
users[0].music_session_id.should == later_session.id
users[0].id.should == creator2.id
users[0].latency.should == 22 # (5 + 30 + 10) / 2
users[0].full_score.should == 45 # (5 + 30 + 10)
users[0].audio_latency.should == 10
users[0].internet_score.should == 30
users[1].music_session_id == earlier_session.id
users[1].id.should == creator.id
users[1].latency.should == 15 # (5 + 20 + 5) / 2
users[1].full_score.should == 30 # (5 + 20 + 5)
users[1].audio_latency.should == 5
users[1].internet_score.should == 20
end
end
end

View File

@ -411,6 +411,39 @@ describe MusicSession do
end
end
def session_with_scores(user, music_session_id)
ActiveRecord::Base.transaction do
return MusicSession.session_with_scores(user, music_session_id)
end
end
describe "session_with_scores", no_transaction: true do
let(:conn) { FactoryGirl.create(:connection, user: creator) }
let(:searcher) { FactoryGirl.create(:user) }
let(:searcher_conn) { FactoryGirl.create(:connection, user: searcher, ip_address: '2.2.2.2') }
let(:default_opts) { {client_id: searcher_conn.client_id} }
let(:network_score) { 20 }
before(:each) do
Score.createx(conn.locidispid, conn.client_id, conn.addr, searcher_conn.locidispid, searcher_conn.client_id, searcher_conn.addr, network_score, nil, nil, {auserid: creator.id, buserid: searcher.id})
end
it "invalid session ID" do
expect {session_with_scores(searcher, 'blah')}.to raise_error(ActiveRecord::RecordNotFound)
end
it "one session with scores" do
creator.last_jam_locidispid = conn.locidispid
creator.save!
session = FactoryGirl.create(:music_session, creator: creator)
music_session, user_scores = session_with_scores(searcher, session.id)
music_session.latency.should be_nil # we don't return music_session.latency with session_with_scores, because it's used for sorting among many sessions
user_scores.length.should == 1
user_scores[creator.id][:full_score].should == (network_score + searcher.last_jam_audio_latency + creator.last_jam_audio_latency )
end
end
describe "sms_index", no_transaction: true do
describe "simple" do
@ -461,9 +494,9 @@ describe MusicSession do
music_sessions, user_scores = sms(searcher, default_opts)
music_sessions.length.should == 1
music_sessions[0].tag.should == 3 # open session sort
music_sessions[0].latency.should == (network_score + searcher.last_jam_audio_latency + creator.last_jam_audio_latency ) / 2
music_sessions[0].latency.should == (network_score + searcher.last_jam_audio_latency + creator.last_jam_audio_latency )
user_scores.length.should == 1
user_scores[creator.id][:latency].should == (network_score + searcher.last_jam_audio_latency + creator.last_jam_audio_latency ) / 2
user_scores[creator.id][:full_score].should == (network_score + searcher.last_jam_audio_latency + creator.last_jam_audio_latency )
end
it "filters sessions in the past" do
@ -504,19 +537,19 @@ describe MusicSession do
music_sessions, user_scores = sms(searcher, default_opts)
music_sessions.length.should == 1
music_sessions[0].tag.should == 3 # open session sort
music_sessions[0].latency.should == (network_score + searcher.last_jam_audio_latency + creator.last_jam_audio_latency ) / 2
music_sessions[0].latency.should == (network_score + searcher.last_jam_audio_latency + creator.last_jam_audio_latency )
user_scores.length.should == 2 # the creator, and the invitee
user_scores[creator.id][:latency].should == (network_score + searcher.last_jam_audio_latency + creator.last_jam_audio_latency ) / 2
user_scores[invitee.id][:latency].should == ((network_score + searcher.last_jam_audio_latency + invitee.last_jam_audio_latency ) / 2).ceil
user_scores[creator.id][:full_score].should == (network_score + searcher.last_jam_audio_latency + creator.last_jam_audio_latency )
user_scores[invitee.id][:full_score].should == ((network_score + searcher.last_jam_audio_latency + invitee.last_jam_audio_latency )).ceil
#search with the invitee this time.
invitee_conn = FactoryGirl.create(:connection, user: invitee, ip_address: '3.3.3.3', locidispid: invitee.last_jam_locidispid)
music_sessions, user_scores = sms(invitee, {client_id: invitee_conn.client_id})
music_sessions.length.should == 1
music_sessions[0].tag.should == 2 # invited sort
music_sessions[0].latency.should == ((network_score + invitee.last_jam_audio_latency + creator.last_jam_audio_latency ) / 2).ceil
music_sessions[0].latency.should == ((network_score + invitee.last_jam_audio_latency + creator.last_jam_audio_latency )).ceil
user_scores.length.should == 1 # the creator, and the invitee
user_scores[creator.id][:latency].should == ((network_score + invitee.last_jam_audio_latency + creator.last_jam_audio_latency ) / 2).ceil
user_scores[creator.id][:full_score].should == ((network_score + invitee.last_jam_audio_latency + creator.last_jam_audio_latency )).ceil
end
it "does not show when it goes active" do
@ -631,20 +664,20 @@ describe MusicSession do
music_session = music_sessions[0]
music_session.should == music_session_1
music_session.tag.should == 1 # RSVP
music_session.latency.should == (bad_network_score + searcher_1.last_jam_audio_latency + creator_1.last_jam_audio_latency ) / 2
music_session.latency.should == (bad_network_score + searcher_1.last_jam_audio_latency + creator_1.last_jam_audio_latency )
music_session = music_sessions[1]
music_session.should == music_session_2
music_session.tag.should == 2 # INVITE
music_session.latency.should == (fair_network_score + searcher_1.last_jam_audio_latency + creator_2.last_jam_audio_latency ) / 2
music_session.latency.should == (fair_network_score + searcher_1.last_jam_audio_latency + creator_2.last_jam_audio_latency )
music_session = music_sessions[2]
music_session.should == music_session_3
music_session.tag.should == 3 # OPEN
music_session.latency.should == (good_network_score + searcher_1.last_jam_audio_latency + creator_3.last_jam_audio_latency ) / 2
music_session.latency.should == (good_network_score + searcher_1.last_jam_audio_latency + creator_3.last_jam_audio_latency )
user_scores.length.should == 3 # the creator, and the invitee
user_scores[creator_1.id][:latency].should == (bad_network_score + searcher_1.last_jam_audio_latency + creator_1.last_jam_audio_latency ) / 2
user_scores[creator_1.id][:full_score].should == (bad_network_score + searcher_1.last_jam_audio_latency + creator_1.last_jam_audio_latency )
# let's make music_session_3 invisible, and verify the count goes to 2
music_session_3.open_rsvps = false

View File

@ -159,12 +159,20 @@ def create_phony_database
GeoIpBlocks.connection.execute("select generate_scores_dataset()").check
end
def austin_ip
IPAddr.new(0x0FFFFFFF, Socket::AF_INET).to_s
end
def dallas_ip
IPAddr.new(0x1FFFFFFF, Socket::AF_INET).to_s
end
# gets related models for an IP in the 1st block from the scores_better_test_data.sql
def austin_geoip
geoiplocation = GeoIpLocations.find_by_locid(17192)
geoipblock = GeoIpBlocks.find_by_locid(17192)
jamisp = JamIsp.find_by_beginip(geoipblock.beginip)
{jamisp: jamisp, geoiplocation: geoiplocation, geoipblock: geoipblock }
{jamisp: jamisp, geoiplocation: geoiplocation, geoipblock: geoipblock, locidispid: Score.compute_locidispid(geoiplocation.locid, jamisp.coid)}
end
# gets related models for an IP in the 1st block from the scores_better_test_data.sql
@ -172,7 +180,7 @@ def dallas_geoip
geoiplocation = GeoIpLocations.find_by_locid(667)
geoipblock = GeoIpBlocks.find_by_locid(667)
jamisp = JamIsp.find_by_beginip(geoipblock.beginip)
{jamisp: jamisp, geoiplocation: geoiplocation, geoipblock: geoipblock}
{jamisp: jamisp, geoiplocation: geoiplocation, geoipblock: geoipblock, locidispid: Score.compute_locidispid(geoiplocation.locid, jamisp.coid)}
end
# attempts to make the creation of a score more straightforward.

View File

@ -111,15 +111,15 @@ def app_config
end
def max_good_full_score
20
40
end
def max_yellow_full_score
35
70
end
def max_red_full_score
50
100
end
private

View File

@ -9,6 +9,7 @@
var logger = context.JK.logger;
var rest = context.JK.Rest();
var sessionUtils = context.JK.SessionUtils;
var helpBubble = context.JK.HelpBubbleHelper;
var sessionId = null;
var sessionData = null;
var rsvpData = null;
@ -24,15 +25,6 @@
var inviteMusiciansUtil = null;
var friendInput=null;
var LATENCY = {
GOOD : {description: "GOOD", style: "latency-green", min: 0.0, max: 20.0},
MEDIUM : {description: "MEDIUM", style: "latency-yellow", min: 20.0, max: 40.0},
POOR : {description: "POOR", style: "latency-red", min: 40.0, max: 10000000000.0},
UNREACHABLE: {description: "UNREACHABLE", style: "latency-grey", min: -1, max: -1},
UNKNOWN: {description: "UNKNOWN", style: "latency-grey", min: -2, max: -2}
};
function beforeShow(data) {
sessionId = data.id;
loadSessionData();
@ -146,14 +138,18 @@
.done(function(rsvpResponse) {
rsvpData = rsvpResponse;
renderSession();
app.user()
.done(function(userMe) {
renderSession(userMe);
})
})
.fail(app.ajaxError);
})
.fail(app.ajaxError);
}
function renderSession() {
function renderSession(userMe) {
var hasPending = false;
var isOwner = false;
if (sessionData.user_id == context.JK.currentUserId) {
@ -202,65 +198,41 @@
var sessionInvitedHtml = generateSessionInvited();
var sessionPropertiesHtml = generateSessionProperties();
var template = context._.template(
var $template = $(context._.template(
$('#template-account-session-detail').html(),
{ has_pending: hasPending, is_owner: isOwner, notification_msg: sessionData.notification_msg,
pending_rsvps: pendingRsvpHtml, session_rsvps: sessionRsvpsHtml, still_needed: sessionNeededHtml,
invited_users: sessionInvitedHtml, session_properties: sessionPropertiesHtml, id: sessionData.id},
{variable: 'data'}
);
));
$sessionDetail.html(template);
var $offsetParent = $($sessionDetail).closest('.content-body');
$.each($template.find('.latency'), function(index, latencyBadge) {
var $latencyBadge = $(latencyBadge);
var full_score = $latencyBadge.attr('data-full-score') || null;
var internet_score = $latencyBadge.attr('data-internet-score') || null;
var audio_latency = $latencyBadge.attr('data-audio-latency') || null;
var latencyBadgeUserId = $latencyBadge.attr('data-user-id');
var scoreOptions = {offsetParent: $offsetParent};
helpBubble.scoreBreakdown($latencyBadge, context.JK.currentUserId == latencyBadgeUserId, full_score, userMe.last_jam_audio_latency, audio_latency, internet_score, scoreOptions);
});
$sessionDetail.html($template);
events();
}
function createLatency(user) {
var latencyStyle = LATENCY.UNREACHABLE.style, latencyDescription = LATENCY.UNREACHABLE.description
if (user.id === context.JK.currentUserId) {
latencyStyle = LATENCY.GOOD.style, latencyDescription = LATENCY.GOOD.description;
}
else {
var latency = user.latency;
if (!latency || latency === 1000) {
// 1000 is a magical number returned by new scoring API to indicate one or more people in the session have an unknown score
latencyDescription = LATENCY.UNKNOWN.description;
latencyStyle = LATENCY.UNKNOWN.style;
}
else {
if (latency <= LATENCY.GOOD.max) {
latencyDescription = LATENCY.GOOD.description;
latencyStyle = LATENCY.GOOD.style;
}
else if (latency > LATENCY.MEDIUM.min && latency <= LATENCY.MEDIUM.max) {
latencyDescription = LATENCY.MEDIUM.description;
latencyStyle = LATENCY.MEDIUM.style;
}
else {
latencyDescription = LATENCY.POOR.description;
latencyStyle = LATENCY.POOR.style;
}
}
}
return {
latency_style: latencyStyle,
latency_text: latencyDescription
};
}
function generatePendingRsvps() {
var resultHtml = "";
var rsvpHtml = "";
var instrumentLogoHtml = "";
var latencyHtml = "";
$.each(sessionData.pending_rsvp_requests, function(index, request) {
if (request.user_id != context.JK.currentUserId) {
if ("instrument_list" in request && request.instrument_list != null) {
console.log(request.instrument_list)
$.each(request.instrument_list, function (index, instrument) {
$.each(sessionData.pending_rsvp_requests, function(index, pending_rsvp_request) {
if (pending_rsvp_request.user_id != context.JK.currentUserId) {
if ("instrument_list" in pending_rsvp_request && pending_rsvp_request.instrument_list != null) {
$.each(pending_rsvp_request.instrument_list, function (index, instrument) {
var instrumentId = instrument == null ? null : instrument.id;
var inst = context.JK.getInstrumentIcon24(instrumentId);
instrumentLogoHtml += '<img data-instrument-id="' + instrumentId + '" src="' + inst + '" width="24" height="24" />&nbsp;';
@ -269,17 +241,17 @@
latencyHtml = context._.template(
$("#template-account-session-latency").html(),
createLatency(request.user),
$.extend(sessionUtils.createLatency(pending_rsvp_request.user), pending_rsvp_request.user),
{variable: 'data'}
);
var avatar_url = context.JK.resolveAvatarUrl(request.user.photo_url);
var avatar_url = context.JK.resolveAvatarUrl(pending_rsvp_request.user.photo_url);
rsvpHtml = context._.template(
$("#template-account-pending-rsvp").html(),
{user_id: request.user_id, avatar_url: avatar_url,
user_name: request.user.name, instruments: instrumentLogoHtml,
latency: latencyHtml, request_id: request.id},
{user_id: pending_rsvp_request.user_id, avatar_url: avatar_url,
user_name: pending_rsvp_request.user.name, instruments: instrumentLogoHtml,
latency: latencyHtml, request_id: pending_rsvp_request.id},
{variable: 'data'}
);
@ -296,9 +268,9 @@
var rsvpHtml = "";
var instrumentLogoHtml = "";
var latencyHtml = "";
$.each(sessionData.approved_rsvps, function(index, request) {
if ("instrument_list" in request) {
$.each(request.instrument_list, function(index, instrument) {
$.each(sessionData.approved_rsvps, function(index, approved_rsvp) {
if ("instrument_list" in approved_rsvp) {
$.each(approved_rsvp.instrument_list, function(index, instrument) {
var instrumentId = instrument == null ? null : instrument.id;
var inst = context.JK.getInstrumentIcon24(instrumentId);
instrumentLogoHtml += '<img src="' + inst + '" width="24" height="24" />&nbsp;';
@ -307,16 +279,16 @@
latencyHtml = context._.template(
$("#template-account-session-latency").html(),
createLatency(request),
$.extend(sessionUtils.createLatency(approved_rsvp), approved_rsvp),
{variable: 'data'}
);
var avatar_url = request.resolved_photo_url;
var avatar_url = approved_rsvp.resolved_photo_url;
var request_id = null;
$.each(rsvpData, function(index, rsvp) {
if (rsvp.user_id == request.id) {
if (rsvp.user_id == approved_rsvp.id) {
var approved = true;
$.each(rsvp, function(index, rsvp_slot) {
if (rsvp_slot.approved == false) {
@ -332,8 +304,8 @@
rsvpHtml = context._.template(
$("#template-account-session-rsvp").html(),
{id: request.id, avatar_url: avatar_url,
user_name: request.name, instruments: instrumentLogoHtml,
{id: approved_rsvp.id, avatar_url: avatar_url,
user_name: approved_rsvp.name, instruments: instrumentLogoHtml,
latency: latencyHtml, is_owner: sessionData.isOwner, request_id: request_id},
{variable: 'data'}
);

View File

@ -13,6 +13,8 @@
var page_num=1, page_count=0;
var textMessageDialog = null;
var $results = null;
var helpBubble = context.JK.HelpBubbleHelper;
var sessionUtils = context.JK.SessionUtils;
function loadMusicians(queryString) {
// squelch nulls and undefines
@ -85,23 +87,6 @@
return Math.round(score / 2) + " ms";
}
function score_to_color(score) {
// these are raw scores as reported by client (round trip times)
if (score == null) return "purple";
if (0 < score && score <= 40) return "green";
if (40 < score && score <= 70) return "yellow";
if (70 < score && score <= 100) return "red";
return "blue";
}
function score_to_color_alt(score) {
// these are raw scores as reported by client (round trip times)
if (score == null) return "missing";
if (0 < score && score <= 40) return "good";
if (40 < score && score <= 70) return "moderate";
if (70 < score && score <= 100) return "poor";
return "unacceptable";
}
function formatLocation(musician) {
if(musician.city && musician.state) {
@ -167,6 +152,8 @@
var musician_actions = context.JK.fillTemplate(aTemplate, actionVals);
var full_score = musician['full_score'];
var scoreInfo = sessionUtils.scoreInfo(full_score, false)
mVals = {
avatar_url: context.JK.resolveAvatarUrl(musician.photo_url),
profile_url: "/client#/profile/" + musician.id,
@ -182,22 +169,20 @@
musician_follow_template: follows,
musician_action_template: musician_actions,
musician_one_way_score: score_to_text(full_score),
musician_score_color: score_to_color(full_score),
musician_score_color_alt: score_to_color_alt(full_score)
musician_score_color: scoreInfo.icon_name,
musician_score_color_alt: scoreInfo.description,
latency_style: scoreInfo.latency_style
};
var $rendering = $(context.JK.fillTemplate(mTemplate, mVals))
var $offsetParent = $results.closest('.content');
var options = {positions: ['top', 'bottom', 'right', 'left'], offsetParent: $offsetParent};
var scoreOptions = {positions: ['right', 'top', 'bottom', 'left'], offsetParent: $offsetParent, width:'600px'};
var scoreOptions = {offsetParent: $offsetParent};
context.JK.helpBubble($('.follower-count', $rendering), 'musician-follower-count', {}, options);
context.JK.helpBubble($('.friend-count', $rendering), 'musician-friend-count', {}, options);
context.JK.helpBubble($('.recording-count', $rendering), 'musician-recording-count', {}, options);
context.JK.helpBubble($('.session-count', $rendering), 'musician-session-count', {}, options);
context.JK.helpBubble($('.score-count', $rendering), 'musician-score-count',
{full_score: full_score ? Math.round(full_score / 2) : null, my_gear_latency: myAudioLatency, their_gear_latency:musician['audio_latency'], internet_latency: musician['score']},
scoreOptions)
helpBubble.scoreBreakdown($('.score-count', $rendering), false, full_score, myAudioLatency, musician['audio_latency'], musician['score'], scoreOptions);
$results.append($rendering);
}

View File

@ -94,11 +94,11 @@
}
function renderActiveSessions(sessions) {
$.each(sessions, function(i, session) {
sessionList.renderActiveSession(session, $(CATEGORY.ACTIVE.id));
$.each(sessions.sessions, function(i, session) {
sessionList.renderActiveSession(session, $(CATEGORY.ACTIVE.id), sessions.my_audio_latency);
});
afterLoadActiveSessions(sessions);
afterLoadActiveSessions(sessions.sessions);
}
function buildActiveSessionsQuery() {
@ -162,10 +162,10 @@
}
function renderScheduledSessions(sessions) {
$.each(sessions, function(i, session) {
sessionList.renderInactiveSession(session, $(CATEGORY.SCHEDULED.id));
$.each(sessions.sessions, function(i, session) {
sessionList.renderInactiveSession(session, $(CATEGORY.SCHEDULED.id), undefined, sessions.my_audio_latency);
});
afterLoadScheduledSessions(sessions);
afterLoadScheduledSessions(sessions.sessions);
}
function buildScheduledSessionsQuery() {

View File

@ -0,0 +1,29 @@
/**
* HelpBubble helper functions, for help bubbles with lots of arguments or logic
*/
(function (context, $) {
"use strict";
context.JK = context.JK || {};
var helpBubble = {};
var rest = new context.JK.Rest();
context.JK.HelpBubbleHelper = helpBubble;
var logger = context.JK.logger;
var defaultScoreBreakDownOptions = {positions: ['right', 'top', 'bottom', 'left'], width:'600px', closeWhenOthersOpen: true };
helpBubble.scoreBreakdown = function($element, isCurrentUser, full_score, myAudioLatency, otherAudioLatency, internetScore, options) {
options = options || {};
options = $.extend({}, defaultScoreBreakDownOptions, options)
if(isCurrentUser) {
context.JK.helpBubble($element, 'musician-score-self',
{full_score: full_score ? Math.round(full_score / 2) : null, my_gear_latency: myAudioLatency, their_gear_latency: otherAudioLatency, internet_latency: internetScore},
options);
} else {
context.JK.helpBubble($element, 'musician-score-count',
{full_score: full_score ? Math.round(full_score / 2) : null, my_gear_latency: myAudioLatency, their_gear_latency: otherAudioLatency, internet_latency: internetScore},
options);
}
}
})(window, jQuery);

View File

@ -6,10 +6,11 @@
context.JK.SessionList = function(app) {
var EVENTS = context.JK.EVENTS;
var gearUtils = context.JK.GearUtils;
var sessionUtils = context.JK.SessionUtils;
var helpBubble = context.JK.HelpBubbleHelper;
var logger = context.JK.logger;
var rest = context.JK.Rest();
var ui = new context.JK.UIHelper(app);
var sessionUtils = context.JK.SessionUtils;
var $activeSessionTemplate = $('#template-active-session-row');
var $inactiveSessionTemplate = $('#template-inactive-session-row');
var $notationFileTemplate = $('#template-notation-files');
@ -19,15 +20,7 @@
var showJoinLink = true;
var showRsvpLink = true;
var LATENCY = {
GOOD : {description: "GOOD", style: "latency-green", min: 0.0, max: 20.0},
MEDIUM : {description: "MEDIUM", style: "latency-yellow", min: 20.0, max: 35.0},
POOR : {description: "POOR", style: "latency-red", min: 35.0, max: 50},
UNACCEPTABLE: {description: "UNACCEPTABLE", style: "latency-grey", min: 50, max: 10000000},
UNKNOWN: {description: "UNKNOWN", style: "latency-grey", min: -2, max: -2}
};
function renderActiveSession(session, tbGroup) {
function renderActiveSession(session, tbGroup, myAudioLatency) {
$('#actionHeader', tbGroup).html('JOIN');
@ -95,8 +88,17 @@
sessionVals.in_session_musicians = inSessionUsersHtml.length > 0 ? inSessionUsersHtml : 'N/A';
sessionVals.join_link_display_style = showJoinLink ? "block" : "none";
var row = context.JK.fillTemplate($activeSessionTemplate.html(), sessionVals);
$(tbGroup).append(row);
var $row = $(context.JK.fillTemplate($activeSessionTemplate.html(), sessionVals));
var $offsetParent = $(tbGroup).closest('.content');
var $latencyBadge = $row.find('.latency-value');
var full_score = $latencyBadge.attr('data-full-score') || null;
var internet_score = $latencyBadge.attr('data-internet-score') || null;
var audio_latency = $latencyBadge.attr('data-audio-latency') || null;
var latencyBadgeUserId = $latencyBadge.attr('data-user-id');
var scoreOptions = {offsetParent: $offsetParent};
helpBubble.scoreBreakdown($latencyBadge, context.JK.currentUserId == latencyBadgeUserId, full_score, myAudioLatency, audio_latency, internet_score, scoreOptions);
$(tbGroup).append($row);
if (showJoinLink) {
// wire up the Join Link to the T&Cs dialog
@ -127,7 +129,7 @@
}
}
function renderInactiveSession(session, tbGroup, $rowToUpdate) {
function renderInactiveSession(session, tbGroup, $rowToUpdate, myAudioLatency) {
var openSlots = false;
var hasInvitation = false;
@ -195,15 +197,25 @@
var sessionVals = buildSessionObject(session, notationFileHtml, rsvpUsersHtml, openSlotsHtml, latencyHtml);
sessionVals.scheduled_start = session.pretty_scheduled_start_with_timezone;
var row = context.JK.fillTemplate($inactiveSessionTemplate.html(), sessionVals);
var $row = $(context.JK.fillTemplate($inactiveSessionTemplate.html(), sessionVals));
var $offsetParent = $(tbGroup).closest('.content');
var $latencyBadge = $row.find('.latency-value');
var full_score = $latencyBadge.attr('data-full-score') || null;
var internet_score = $latencyBadge.attr('data-internet-score') || null;
var audio_latency = $latencyBadge.attr('data-audio-latency') || null;
var latencyBadgeUserId = $latencyBadge.attr('data-user-id');
var scoreOptions = {offsetParent: $offsetParent};
helpBubble.scoreBreakdown($latencyBadge, context.JK.currentUserId == latencyBadgeUserId, full_score, myAudioLatency, audio_latency, internet_score, scoreOptions);
// initial page load
if (!$rowToUpdate) {
$(tbGroup).append(row);
$(tbGroup).append($row);
}
// inline update after an RSVP submission / cancellation
else {
$rowToUpdate.replaceWith(row);
$rowToUpdate.replaceWith($row);
}
var $parentRow = $('tr[data-session-id=' + session.id + ']', tbGroup);
@ -219,7 +231,7 @@
.one(EVENTS.RSVP_CANCELED, function() {
rest.getSessionHistory(session.id)
.done(function(response) {
renderInactiveSession(response, tbGroup, $parentRow);
renderInactiveSession(response, tbGroup, $parentRow, myAudioLatency);
});
})
.one(EVENTS.DIALOG_CLOSED, function() {
@ -236,7 +248,7 @@
.one(EVENTS.RSVP_CANCELED, function() {
rest.getSessionHistory(session.id)
.done(function(response) {
renderInactiveSession(response, tbGroup, $parentRow);
renderInactiveSession(response, tbGroup, $parentRow, myAudioLatency);
});
})
.one(EVENTS.DIALOG_CLOSED, function() {
@ -263,7 +275,7 @@
.one(EVENTS.RSVP_SUBMITTED, function() {
rest.getSessionHistory(session.id)
.done(function(response) {
renderInactiveSession(response, tbGroup, $parentRow);
renderInactiveSession(response, tbGroup, $parentRow, myAudioLatency);
});
})
.one(EVENTS.DIALOG_CLOSED, function() {
@ -318,7 +330,7 @@
};
var musicianHtml = context.JK.fillTemplate($musicianTemplate.html(), musicianVals);
var latencyHtml = context.JK.fillTemplate($latencyTemplate.html(), createLatency(participant.user));
var latencyHtml = context._.template($latencyTemplate.html(), $.extend(sessionUtils.createLatency(participant.user), participant.user), { variable: 'data' });
return [musicianHtml, latencyHtml];
}
@ -349,51 +361,11 @@
};
var musicianHtml = context.JK.fillTemplate($musicianTemplate.html(), musicianVals);
var latencyHtml = context.JK.fillTemplate($latencyTemplate.html(), createLatency(user));
var latencyHtml = context._.template($latencyTemplate.html(), $.extend(sessionUtils.createLatency(user), user), { variable: 'data' });
return [musicianHtml, latencyHtml];
}
function createLatency(user) {
var latencyStyle;
var latencyDescription;
if (user.id === context.JK.currentUserId) {
latencyStyle = LATENCY.GOOD.style, latencyDescription = LATENCY.GOOD.description;
}
else {
var latency = user.latency;
if (!latency) {
latencyDescription = LATENCY.UNKNOWN.description;
latencyStyle = LATENCY.UNKNOWN.style;
}
else if (latency <= LATENCY.GOOD.max) {
latencyDescription = LATENCY.GOOD.description;
latencyStyle = LATENCY.GOOD.style;
}
else if (latency > LATENCY.MEDIUM.min && latency <= LATENCY.MEDIUM.max) {
latencyDescription = LATENCY.MEDIUM.description;
latencyStyle = LATENCY.MEDIUM.style;
}
else if (latency > LATENCY.POOR.min && latency <= LATENCY.UNACCEPTABLE.max) {
latencyDescription = LATENCY.POOR.description;
latencyStyle = LATENCY.POOR.style;
}
else {
latencyStyle = LATENCY.UNREACHABLE.style
latencyDescription = LATENCY.UNREACHABLE.description
}
}
return {
latency_style: latencyStyle,
latency_text: latencyDescription
};
}
function createNotationFile(notation) {
var notationVals = {
file_url: notation.file_url,

View File

@ -11,6 +11,17 @@
context.JK.SessionUtils = sessionUtils;
var logger = context.JK.logger;
var LATENCY = sessionUtils.LATENCY = {
ME : {description: "ME", style: "latency-me", min: -1, max: -1},
GOOD : {description: "GOOD", style: "latency-good", min: 0.0, max: 40.0},
MEDIUM : {description: "FAIR", style: "latency-fair", min: 40.0, max: 70.0},
POOR : {description: "POOR", style: "latency-poor", min: 70.0, max: 100},
UNACCEPTABLE: {description: "UNACCEPTABLE", style: "latency-unacceptable", min: 100, max: 10000000},
UNKNOWN: {description: "UNKNOWN", style: "latency-unknown", min: -2, max: -2}
};
sessionUtils.createOpenSlot = function($openSlotsTemplate, slot) {
var inst = context.JK.getInstrumentIcon24(slot.instrument_id);
@ -52,4 +63,58 @@
}
}
sessionUtils.scoreInfo = function(full_score, isSameUser) {
var latencyDescription;
var latencyStyle;
var iconName;
var description;
if(isSameUser) {
latencyDescription = LATENCY.ME.description;
latencyStyle = LATENCY.ME.style;
iconName = 'purple';
description = 'me';
}
else if (!full_score) {
latencyDescription = LATENCY.UNKNOWN.description;
latencyStyle = LATENCY.UNKNOWN.style;
iconName = 'purple'
description = 'missing'
}
else if (full_score <= LATENCY.GOOD.max) {
latencyDescription = LATENCY.GOOD.description;
latencyStyle = LATENCY.GOOD.style;
iconName = 'green'
description = 'good'
}
else if (full_score <= LATENCY.MEDIUM.max) {
latencyDescription = LATENCY.MEDIUM.description;
latencyStyle = LATENCY.MEDIUM.style;
iconName = 'yellow';
description = 'fair'
}
else if (full_score <= LATENCY.POOR.max) {
latencyDescription = LATENCY.POOR.description;
latencyStyle = LATENCY.POOR.style;
iconName = 'red'
description = 'poor'
}
else {
latencyStyle = LATENCY.UNACCEPTABLE.style;
latencyDescription = LATENCY.UNACCEPTABLE.description;
iconName = 'blue'
description = 'unacceptable'
}
return {
latency_style: latencyStyle,
latency_text: latencyDescription,
icon_name: iconName,
description: description
};
}
sessionUtils.createLatency = function(user) {
return sessionUtils.scoreInfo(user.full_score, user.id === context.JK.currentUserId)
}
})(window, jQuery);

View File

@ -110,6 +110,10 @@
options = {}
}
$element.on('remove', function() {
$element.btOff();
})
var helpText = context._.template($('#template-help-' + templateName).html(), data, { variable: 'data' });
var holder = $('<div class="hover-bubble help-bubble"></div>');

View File

@ -9,15 +9,11 @@
var logger = context.JK.logger;
var rest = JK.Rest();
var ui = new context.JK.UIHelper(app);
var sessionUtils = context.JK.SessionUtils;
var helpBubble = context.JK.HelpBubbleHelper;
var $btnAction = $("#btn-action");
var LATENCY = {
GOOD : {description: "GOOD", style: "latency-green", min: 0.0, max: 20.0},
MEDIUM : {description: "MEDIUM", style: "latency-yellow", min: 20.0, max: 40.0},
POOR : {description: "POOR", style: "latency-red", min: 40.0, max: 10000000000.0},
UNREACHABLE: {description: "UNREACHABLE", style: "latency-grey", min: -1, max: -1},
UNKNOWN: {description: "UNKNOWN", style: "latency-grey", min: -2, max: -2}
};
var $templateLatencyDetail = null;
var $landingSidebar = null;
function addComment() {
var comment = $("#txtSessionInfoComment").val();
@ -66,70 +62,48 @@
}
// this is done post-page load to allow websocket connection to be established
function addLatencyDetails() {
rest.getSessionHistory(musicSessionId)
.done(function(session) {
if (session.approved_rsvps && session.approved_rsvps.length > 0) {
// loop through each record in RSVPs, which has already been rendered, and extract
// the user ID
$('.rsvp-details').each(function(index) {
var $user = $(this).find('div[hoveraction="musician"]');
var userId = $user.attr('user-id');
var latency = null;
var latencyStyle = LATENCY.UNKNOWN.style;
var latencyDescription = LATENCY.UNKNOWN.description;
function addLatencyDetails(session) {
if (session.approved_rsvps && session.approved_rsvps.length > 0) {
// loop through each record in RSVPs, which has already been rendered, and extract
// the user ID
$('.rsvp-details').each(function(index) {
var $user = $(this).find('div[hoveraction="musician"]');
var userId = $user.attr('user-id');
// default current user to GOOD
if (userId === context.JK.currentUserId) {
latencyStyle = LATENCY.GOOD.style;
latencyDescription = LATENCY.GOOD.description;
}
else {
// find the corresponding user in the API response payload
for (var i=0; i < session.approved_rsvps.length; i++) {
if (session.approved_rsvps.id === userId) {
latency = session.approved_rsvps[i].latency;
break;
}
}
if (latency) {
if (latency <= LATENCY.GOOD.max) {
latencyDescription = LATENCY.GOOD.description;
latencyStyle = LATENCY.GOOD.style;
}
else if (latency > LATENCY.MEDIUM.min && latency <= LATENCY.MEDIUM.max) {
latencyDescription = LATENCY.MEDIUM.description;
latencyStyle = LATENCY.MEDIUM.style;
}
else {
latencyDescription = LATENCY.POOR.description;
latencyStyle = LATENCY.POOR.style;
}
}
else {
latencyStyle = LATENCY.UNKNOWN.style;
latencyDescription = LATENCY.UNKNOWN.description;
}
}
var user = null;
for (var i=0; i < session.approved_rsvps.length; i++) {
if (session.approved_rsvps[i].id === userId) {
user = session.approved_rsvps[i];
break;
}
}
if(user) {
// add the latency details for this user to the UI
var latencyHtml = context.JK.fillTemplate($('#template-latency-detail').html(), {
latencyStyle: latencyStyle,
latencyDescription: latencyDescription
});
$(this).find('.latency-tags').append(latencyHtml);
});
}
});
var $latencyHtml = $(context.JK.fillTemplate($templateLatencyDetail.html(),
sessionUtils.createLatency(user)));
var $latencyBadge = $latencyHtml.find('.latency');
app.user().done(function(userMe) {
helpBubble.scoreBreakdown($latencyBadge, context.JK.currentUserId == user.id, user.full_score, userMe.last_jam_audio_latency, user.audio_latency, user.internet_score, {offsetParent: $landingSidebar});
})
$(this).find('.latency-tags').append($latencyHtml);
}
else {
logger.warn("unable to find user in approved_rsvps");
}
});
}
}
function initialize() {
registerScheduledSessionComment();
var $parent = $('.landing-sidebar');
context.JK.bindHoverEvents($parent);
context.JK.setInstrumentAssetPath($('.instrument-icon', $parent));
$landingSidebar = $('.landing-sidebar');
$templateLatencyDetail = $('#template-latency-detail');
context.JK.bindHoverEvents($landingSidebar);
context.JK.setInstrumentAssetPath($('.instrument-icon', $landingSidebar));
// render comments
$(".landing-comment-scroller").empty();
@ -141,6 +115,8 @@
context.JK.resolveAvatarUrl(val.creator.photo_url), $.timeago(val.created_at), true);
});
}
addLatencyDetails(response);
})
.fail(function(xhr) {
@ -212,8 +188,6 @@
$("#txtSessionComment").blur();
}
});
addLatencyDetails();
}
this.initialize = initialize;

View File

@ -17,6 +17,7 @@
//= require jquery.custom-protocol
//= require jquery.ba-bbq
//= require jquery.icheck
//= require jquery.bt
//= require AAA_Log
//= require AAC_underscore
//= require globals
@ -43,6 +44,8 @@
//= require custom_controls
//= require ga
//= require jam_rest
//= require session_utils
//= require helpBubbleHelper
//= require facebook_rest
//= require landing/init
//= require landing/signup

View File

@ -294,8 +294,9 @@
.latency {
font-size: 15px;
height: 16px;
padding-left: 4px;
padding: 3px;
width: 100%;
@include border-radius(2px);
}
#session-rsvps, #still-needed, #invited-users {

View File

@ -37,6 +37,13 @@ $border: hsl(210, 50%, 45%);
$narrow-screen: 1000px; // 990 ? 1000 ?
$short-screen: 600px; // toolbars / chrome for x768
$latencyBadgeMe: #dddddd;
$latencyBadgeGood: #71a43b;
$latencyBadgeFair: #cc9900;
$latencyBadgePoor: #980006;
$latencyBadgeUnacceptable: #868686;
$latencyBadgeUnknown: #868686;
@mixin border_box_sizing {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
@ -51,6 +58,44 @@ $short-screen: 600px; // toolbars / chrome for x768
box-sizing: content-box;
}
@mixin border-radius($radius) {
-moz-border-radius:$radius;
-webkit-border-radius: $radius;
border-radius: $radius;
background-clip: padding-box; /* stops bg color from leaking outside the border: */
}
// Single side border-radius
@mixin border-top-radius($radius) {
-webkit-border-top-right-radius: $radius;
border-top-right-radius: $radius;
-webkit-border-top-left-radius: $radius;
border-top-left-radius: $radius;
background-clip: padding-box;
}
@mixin border-right-radius($radius) {
-webkit-border-bottom-right-radius: $radius;
border-bottom-right-radius: $radius;
-webkit-border-top-right-radius: $radius;
border-top-right-radius: $radius;
background-clip: padding-box;
}
@mixin border-bottom-radius($radius) {
-webkit-border-bottom-right-radius: $radius;
border-bottom-right-radius: $radius;
-webkit-border-bottom-left-radius: $radius;
border-bottom-left-radius: $radius;
background-clip: padding-box;
}
@mixin border-left-radius($radius) {
-webkit-border-bottom-left-radius: $radius;
border-bottom-left-radius: $radius;
-webkit-border-top-left-radius: $radius;
border-top-left-radius: $radius;
background-clip: padding-box;
}
@mixin flat_dropdown {
box-shadow: none !important;
color: #666666;

View File

@ -1,3 +1,5 @@
@import 'common.css.scss';
#findSession {
th, td { margin: 4px; padding:4px; }
@ -68,4 +70,8 @@
.end-of-list {
margin-top:0;
}
.latency-value {
@include border-radius(2px);
}
}

View File

@ -1,4 +1,4 @@
.screen {
.screen, body.web {
.bt-wrapper {
.bt-content {
@ -7,10 +7,15 @@
font-size:14px;
p {
margin:1em;
line-height:150%;
font-size:14px;
}
ul {
font-size:14px;
margin-left: 2em;
list-style: disc;
color:#CCCCCC;
}
li {

View File

@ -1,6 +1,71 @@
@import "client/common";
table.findsession-table, table.local-recordings, #account-session-detail {
.latency-unacceptable {
width: 50px;
height: 10px;
font-family:Arial, Helvetica, sans-serif;
font-weight:200;
font-size:11px;
background-color:$latencyBadgeUnacceptable;
text-align:center;
}
.latency-unknown {
width: 50px;
height: 10px;
font-family:Arial, Helvetica, sans-serif;
font-weight:200;
font-size:11px;
background-color:$latencyBadgeUnacceptable;
text-align:center;
}
.latency-good {
width: 50px;
height: 10px;
font-family:Arial, Helvetica, sans-serif;
font-weight:200;
font-size:11px;
background-color:$latencyBadgeGood;
text-align:center;
}
.latency-me {
width: 50px;
height: 10px;
font-family:Arial, Helvetica, sans-serif;
font-weight:200;
font-size:11px;
text-align:center;
background-color:$latencyBadgeMe;
color:black;
}
.latency-fair {
width: 50px;
height: 10px;
font-family:Arial, Helvetica, sans-serif;
font-weight:200;
font-size:11px;
background-color:$latencyBadgeFair;
text-align:center;
}
.latency-poor{
width: 40px;
height: 10px;
font-family:Arial, Helvetica, sans-serif;
font-weight:200;
font-size:11px;
background-color:$latencyBadgePoor;
text-align:center;
}
}
table.findsession-table, table.local-recordings {
width:98%;
height:10%;
@ -80,45 +145,6 @@ table.findsession-table, table.local-recordings {
}
}
.latency-grey {
width: 50px;
height: 10px;
font-family:Arial, Helvetica, sans-serif;
font-weight:200;
font-size:11px;
background-color:#868686;
text-align:center;
}
.latency-green {
width: 50px;
height: 10px;
font-family:Arial, Helvetica, sans-serif;
font-weight:200;
font-size:11px;
background-color:#71a43b;
text-align:center;
}
.latency-yellow {
width: 50px;
height: 10px;
font-family:Arial, Helvetica, sans-serif;
font-weight:200;
font-size:11px;
background-color:#cc9900;
text-align:center;
}
.latency-red {
width: 40px;
height: 10px;
font-family:Arial, Helvetica, sans-serif;
font-weight:200;
font-size:11px;
background-color:#980006;
text-align:center;
}
.avatar-tiny {
float:left;

View File

@ -0,0 +1,56 @@
@import "client/common";
body.web.session_info {
table.musicians {
margin-left:4px;
}
table.musicians td {
border-right:none;
border-top:none;
padding:2px;
vertical-align:middle !important;
}
.latency {
width: 50px;
height: 10px;
font-family:Arial, Helvetica, sans-serif;
font-weight:200;
font-size:11px;
text-align:center;
@include border-radius(2px);
}
.latency-unknown {
background-color:$latencyBadgeUnknown;
}
.latency-unacceptable {
background-color:$latencyBadgeUnacceptable;
}
.latency-good {
background-color:$latencyBadgeGood;
}
.latency-fair{
background-color:$latencyBadgeFair;
}
.latency-poor {
background-color:$latencyBadgePoor;
}
.latency-me {
width: 40px;
height: 10px;
font-family:Arial, Helvetica, sans-serif;
font-weight:200;
font-size:11px;
background-color:$latencyBadgeMe;
text-align:center;
color:black;
}
}

View File

@ -1,54 +1,3 @@
#btnPlayPause {
position: relative;
}
table.musicians {
margin-top:-3px;
}
table.musicians td {
border-right:none;
border-top:none;
padding:2px;
vertical-align:middle !important;
}
.latency-grey {
width: 50px;
height: 10px;
font-family:Arial, Helvetica, sans-serif;
font-weight:200;
font-size:11px;
background-color:#868686;
text-align:center;
}
.latency-green {
width: 50px;
height: 10px;
font-family:Arial, Helvetica, sans-serif;
font-weight:200;
font-size:11px;
background-color:#71a43b;
text-align:center;
}
.latency-yellow {
width: 50px;
height: 10px;
font-family:Arial, Helvetica, sans-serif;
font-weight:200;
font-size:11px;
background-color:#cc9900;
text-align:center;
}
.latency-red {
width: 40px;
height: 10px;
font-family:Arial, Helvetica, sans-serif;
font-weight:200;
font-size:11px;
background-color:#980006;
text-align:center;
}

View File

@ -11,12 +11,14 @@
*= require client/ftue
*= require client/user_dropdown
*= require client/hoverBubble
*= require client/help
*= require web/main
*= require web/footer
*= require web/recordings
*= require web/welcome
#= require web/sessions
*= require web/events
*= require web/session_info
*= require users/signinCommon
*= require dialogs/dialog
*= require landings/landing_page

View File

@ -532,7 +532,13 @@ class ApiMusicSessionsController < ApiController
end
def show_history
@history = MusicSession.find(params[:id])
if current_user
ActiveRecord::Base.transaction do
@history, @user_scores = MusicSession.session_with_scores(current_user, params[:id])
end
else
@history = MusicSession.find(params[:id])
end
end
def claimed_recording_start

View File

@ -1,9 +1,20 @@
module ScoreHelper
# helper method to make finding a user's score fault-tolerant
def user_score(user_id)
def user_score_old(user_id)
@user_scores ||= {}
user = @user_scores[user_id]
user ? user[:latency] : nil
user ? user[:full_score] : nil
end
def user_score(user_id)
@user_scores ||= {}
user = @user_scores[user_id] || {}
{ full_score: user[:full_score], audio_latency: user[:audio_latency], internet_score: user[:internet_score] }
end
def last_jam_audio_latency(user)
user.last_jam_audio_latency ? user.last_jam_audio_latency.round : nil
end
end

View File

@ -1,3 +1,8 @@
object @music_sessions
extends "api_music_sessions/show_history"
node :my_audio_latency do |user|
current_user.last_jam_audio_latency.round if current_user.last_jam_audio_latency
end
child @music_sessions => :sessions do
extends "api_music_sessions/show_history"
end

View File

@ -93,10 +93,9 @@ else
attributes :id, :sender_id, :receiver_id
node do |invitation|
{
latency: user_score(invitation.receiver.id),
receiver_avatar_url: invitation.receiver.resolved_photo_url
}
user_score(invitation.receiver.id).merge({
receiver_avatar_url: invitation.receiver.resolved_photo_url, audio_latency: last_jam_audio_latency(invitation.receiver)
})
end
}
@ -104,11 +103,11 @@ else
attributes :id, :photo_url, :first_name, :last_name, :name, :resolved_photo_url, :rsvp_request_id
node do |user|
{
latency: user_score(user.id),
user_score(user.id).merge({
instrument_list: process_approved_rsvps(user),
rsvp_request_id: JSON.parse(user.rsvp_request_ids)[0] # there must always be a rsvp_request_id; and they should all be the same
}
rsvp_request_id: JSON.parse(user.rsvp_request_ids)[0], # there must always be a rsvp_request_id; and they should all be the same
audio_latency: last_jam_audio_latency(user)
})
end
}
@ -127,7 +126,9 @@ else
attributes :id, :photo_url, :name, :first_name, :last_name
node do |user|
{ latency: user_score(user.id), name: user.name }
user_score(user.id).merge({
name: user.name,
audio_latency: last_jam_audio_latency(user)})
end
}
}
@ -156,7 +157,13 @@ else
attributes :ip_address, :client_id, :joined_session_at
node :user do |connection|
{ :id => connection.user.id, :photo_url => connection.user.photo_url, :name => connection.user.name, :is_friend => connection.user.friends?(current_user), :connection_state => connection.aasm_state, latency: user_score(connection.user.id) }
user_score(connection.user.id).merge({
:id => connection.user.id,
:photo_url => connection.user.photo_url,
:name => connection.user.name,
:is_friend => connection.user.friends?(current_user),
:connection_state => connection.aasm_state,
audio_latency: last_jam_audio_latency(connection.user)})
end
child(:tracks => :tracks) {

View File

@ -1,3 +1,8 @@
object @music_sessions
extends "api_music_sessions/show_history"
node :my_audio_latency do |user|
current_user.last_jam_audio_latency.round if current_user.last_jam_audio_latency
end
child @music_sessions => :sessions do
extends "api_music_sessions/show_history"
end

View File

@ -43,11 +43,11 @@ if @search.musicians_filter_search?
end
node :my_audio_latency do |user|
current_user.last_jam_audio_latency
current_user.last_jam_audio_latency.round if current_user.last_jam_audio_latency
end
child(:results => :musicians) {
attributes :id, :first_name, :last_name, :name, :city, :state, :country, :email, :online, :musician, :photo_url, :biography, :regionname, :score, :full_score, :audio_latency
attributes :id, :first_name, :last_name, :name, :city, :state, :country, :email, :online, :musician, :photo_url, :biography, :regionname, :score, :full_score
node :is_friend do |musician|
@search.is_friend?(musician)
@ -79,6 +79,10 @@ if @search.musicians_filter_search?
node :friend_count do |musician| @search.friend_count(musician) end
node :recording_count do |musician| @search.record_count(musician) end
node :session_count do |musician| @search.session_count(musician) end
node :audio_latency do |musician|
last_jam_audio_latency(musician)
end
}
end

View File

@ -68,3 +68,7 @@ end
child :music_sessions => :sessions do
attributes :id, :description, :musician_access, :approval_required, :fan_access
end
node :last_jam_audio_latency do |user|
user.last_jam_audio_latency.round if user.last_jam_audio_latency
end

View File

@ -159,5 +159,5 @@
%img{src: "{{data.avatar_url}}"}
%script{type: 'text/template', id: 'template-account-session-latency'}
.latency{class: "{{data.latency_style}}"}
.latency{class: "{{data.latency_style}}", 'data-user-id' => "{{data.id}}", 'data-audio-latency' => "{{data.audio_latency || ''}}", 'data-full-score' => "{{data.full_score || ''}}", 'data-internet-score' => "{{data.internet_score || ''}}"}
{{data.latency_text}}

View File

@ -242,8 +242,8 @@
<script type="text/template" id="template-latency">
<tr class="mb15">
<td class="{latency_style}">
{latency_text}
<td class="{{data.latency_style}} latency-value" data-user-id="{{data.id}}" data-audio-latency="{{data.audio_latency || ''}}" data-full-score="{{data.full_score || ''}}" data-internet-score="{{data.internet_score || ''}}">
{{data.latency_text}}
</td>
</tr>
<tr><td><div style='height:5px;'>&nbsp;</div></td></tr>

View File

@ -62,9 +62,9 @@
<div class="help-musician-score-count">
<p>The score shown is the one-way latency (or delay) in milliseconds from you to this user. This score is calculated using the following three values that JamKazam gathers:</p>
<ul>
<li><span class="definition">Your Audio Gear Latency:</span> <span class="measurement my-gear-latency"><span class="measurement-value">{{data.my_gear_latency ? data.my_gear_latency + ' ms': '13 ms*'}}</span> <span class="measurement-absent">{{data.my_gear_latency ? '' : "(you have not qualified any gear, so we picked an average gear latency)"}}</span></span></li>
<li><span class="definition">Their Audio Gear Latency:</span> <span class="measurement their-gear-latency"><span class="measurement-value">{{data.their_gear_latency ? data.their_gear_latency + ' ms': '13 ms*'}}</span> <span class="measurement-absent">{{data.their_gear_latency ? '' : "(they have not qualified any gear, so we picked an average gear latency)"}}</span></span></li>
<li><span class="definition">Round-trip Internet Latency:</span> <span class="measurement internet-latency"><span class="measurement-value">{{data.internet_latency ? data.internet_latency + ' ms': '?'}}</span> <span class="measurement-absent">{{data.internet_latency ? '' : "(we have not scored you with this user yet)"}}</span></span></li>
<li><span class="definition">Your Audio Gear Latency:</span> <span class="measurement my-gear-latency partial"><span class="measurement-value">{{data.my_gear_latency ? data.my_gear_latency + ' ms': '13 ms*'}}</span> <span class="measurement-absent">{{data.my_gear_latency ? '' : "(you have not qualified any gear, so we picked an average gear latency)"}}</span></span></li>
<li><span class="definition">Their Audio Gear Latency:</span> <span class="measurement their-gear-latency partial"><span class="measurement-value">{{data.their_gear_latency ? data.their_gear_latency + ' ms': '13 ms*'}}</span> <span class="measurement-absent">{{data.their_gear_latency ? '' : "(they have not qualified any gear, so we picked an average gear latency)"}}</span></span></li>
<li><span class="definition">Round-trip Internet Latency:</span> <span class="measurement internet-latency partial"><span class="measurement-value">{{data.internet_latency ? data.internet_latency + ' ms': '?'}}</span> <span class="measurement-absent">{{data.internet_latency ? '' : "(we have not scored you with this user yet)"}}</span></span></li>
</ul>
<p> <span class="definition">Total One-Way Latency:</span> <span class="measurement my-gear-latency"><span class="measurement-value">( {{data.my_gear_latency ? data.my_gear_latency: '13'}} + {{data.their_gear_latency ? data.their_gear_latency: '13'}} + {{data.internet_latency ? data.internet_latency: '?'}} ) / 2 = {{data.full_score ? data.full_score + ' ms' : "?"}}</span> <span class="measurement-absent">{{data.full_score ? '' : "(when we don't know internet latency, we don't try to guess your one-way latency)"}}</span></span>
@ -78,4 +78,13 @@
</ul>
</div>
</script>
<script type="text/template" id="template-help-musician-score-self">
<div class="help-musician-score-self">
<p>You are looking at your own account.</p>
<p>Try hovering over other user's latency score to find out your one-way latency to them.</p>
<p><span class="definition">Your Audio Gear Latency:</span> <span class="measurement my-gear-latency"><span class="measurement-value">{{data.my_gear_latency ? data.my_gear_latency + ' ms': '13 ms*'}}</span> <span class="measurement-absent">{{data.my_gear_latency ? '' : "(you have not qualified any gear, so we picked an average gear latency)"}}</span></span></p>
</div>
</script>

View File

@ -24,21 +24,12 @@
<!-- Session Row Template -->
<script type="text/template" id="template-find-musician-row">
<div class="profile-band-list-result musician-list-result">
<div class="profile-band-list-result musician-list-result" data-musician-id={musician_id}>
<div class="f11" data-hint="container">
<div class="left" style="width:63px;margin-top:-12px;">
<!-- avatar -->
<div class="avatar-small"><img src="{avatar_url}" /></div>
</div>
<!-- todo scott vfrs-1455 i need a template tutorial to define the three variables that substitute here:
score:
musician_one_way_score is joined_score / 2;
musician_score_color is green, yellow, red, blue, or purple (depending upon value of joined_score)
musician_score_color_alt is good, moderate, poor, unacceptable,or missing
-->
<!-- <div class="left" style="***help***">
{musician_one_way_score} <img src="../assets/content/icon_{musician_score_color}_score.png" alt="{musician_score_color_alt}" width="12" height="12" align="absmiddle"/>
</div> -->
<div class="right musician-following" style="width: 120px;">
<div class="bold">FOLLOWING:</div>
<table class="musicians" cellpadding="0" cellspacing="5">{musician_follow_template}</table>
@ -62,7 +53,7 @@
<span class="follower-count">{follow_count} <img src="../assets/content/icon_followers.png" alt="follows" width="22" height="12" align="absmiddle" style="margin-right:4px;"/></span>
<span class="recording-count">{recording_count} <img src="../assets/content/icon_recordings.png" alt="recordings" width="12" height="13" align="absmiddle" style="margin-right:4px;"/></span>
<span class="session-count">{session_count} <img src="../assets/content/icon_session_tiny.png" alt="sessions" width="12" height="12" align="absmiddle" style="margin-right:4px;"/></span>
<span class="score-count">{musician_one_way_score} <img src="../assets/content/icon_{musician_score_color}_score.png" alt="{musician_score_color_alt} score" width="12" height="12" align="absmiddle" style="margin-right:4px;"/></span>
<span class="score-count {latency_style}">{musician_one_way_score} <img src="../assets/content/icon_{musician_score_color}_score.png" alt="{musician_score_color_alt} score" width="12" height="12" align="absmiddle" style="margin-right:4px;"/></span>
</div>
<div class="result-list-button-wrapper" data-musician-id={musician_id}>
{musician_action_template}

View File

@ -77,8 +77,10 @@
<%= render "clients/hoverBand" %>
<%= render "clients/hoverSession" %>
<%= render "clients/hoverRecording" %>
<%= render "clients/help" %>
<%= render 'dialogs/dialogs' %>
<script type="text/javascript">
$(function () {

View File

@ -1,3 +1,5 @@
- provide(:page_name, 'session_info')
- unless @music_session.nil?
- provide(:title, @music_session.name)
@ -154,5 +156,4 @@
%script{:type => 'text/template', :id => 'template-latency-detail'}
%table.musicians{:cellpadding => 0, :cellspacing => 0}
%tr.mb15
%td{:class => "{latencyStyle}"} {latencyDescription}
%td{:class => "{latency_style} latency"} {latency_text}

View File

@ -257,9 +257,9 @@ if defined?(Bundler)
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
config.max_good_full_score = 40
config.max_yellow_full_score = 70
config.max_red_full_score = 100
end
end

View File

@ -0,0 +1,17 @@
namespace :password do
desc "Resets every password in the database to jam123. development only!"
task smash: :environment do
raise "This rake taske is only available in development" unless Rails.env == 'development'
pw = 'jam123'
User.all.each do |user|
user.updating_password = true
user.password = pw
user.password_confirmation = pw
user.save
end
end
end

View File

@ -25,7 +25,7 @@ describe ApiMusicSessionsController do
get :ams_index, {client_id: conn.client_id}
response.should be_success
json = JSON.parse(response.body, :symbolize_names => true)
json.length.should == 0
json[:sessions].length.should == 0
end
it "just self" do
@ -36,8 +36,8 @@ describe ApiMusicSessionsController do
get :ams_index, {client_id: conn.client_id}
json = JSON.parse(response.body, :symbolize_names => true)
json.length.should == 1
json[0][:active_music_session][:participants][0][:user][:latency].should be_nil
json[:sessions].length.should == 1
json[:sessions][0][:active_music_session][:participants][0][:user][:full_score].should be_nil
end
it "someone else with no score with self" do
@ -48,8 +48,8 @@ describe ApiMusicSessionsController do
get :ams_index, {client_id: conn.client_id}
json = JSON.parse(response.body, :symbolize_names => true)
json.length.should == 1
json[0][:active_music_session][:participants][0][:user][:latency].should be_nil
json[:sessions].length.should == 1
json[:sessions][0][:active_music_session][:participants][0][:user][:full_score].should be_nil
end
it "someone else with a score with a self" do
@ -61,8 +61,8 @@ describe ApiMusicSessionsController do
get :ams_index, {client_id: conn.client_id}
json = JSON.parse(response.body, :symbolize_names => true)
json.length.should == 1
json[0][:active_music_session][:participants][0][:user][:latency].should == 17.5.ceil
json[:sessions].length.should == 1
json[:sessions][0][:active_music_session][:participants][0][:user][:full_score].should == 35 # 5 + 20 + 10
end
it "someone else with a score with a self, and another with an approved RSVP" do
@ -84,23 +84,23 @@ describe ApiMusicSessionsController do
get :ams_index, {client_id: conn.client_id}
json = JSON.parse(response.body, :symbolize_names => true)
json.length.should == 1
json[0][:active_music_session][:participants][0][:user][:latency].should_not be_nil
json[0][:approved_rsvps].length.should == 2
json[:sessions].length.should == 1
json[:sessions][0][:active_music_session][:participants][0][:user][:full_score].should_not be_nil
json[:sessions][0][:approved_rsvps].length.should == 2
if json[0][:approved_rsvps][0][:id] == third_user.id
found_third_user = json[0][:approved_rsvps][0]
found_creator = json[0][:approved_rsvps][1]
if json[:sessions][0][:approved_rsvps][0][:id] == third_user.id
found_third_user = json[:sessions][0][:approved_rsvps][0]
found_creator = json[:sessions][0][:approved_rsvps][1]
found_creator[:id].should == other.id
else
found_third_user = json[0][:approved_rsvps][1]
found_creator = json[0][:approved_rsvps][0]
found_third_user = json[:sessions][0][:approved_rsvps][1]
found_creator = json[:sessions][0][:approved_rsvps][0]
found_third_user[:id].should == third_user.id
found_creator[:id].should == other.id
end
found_third_user[:latency].should == nil
found_creator[:latency].should == ((network_score + user.last_jam_audio_latency + other.last_jam_audio_latency ) / 2 ).ceil
found_third_user[:full_score].should == nil
found_creator[:full_score].should == ((network_score + user.last_jam_audio_latency + other.last_jam_audio_latency )).ceil
end
end
@ -111,7 +111,7 @@ describe ApiMusicSessionsController do
get :sms_index, {client_id: conn.client_id}
response.should be_success
json = JSON.parse(response.body, :symbolize_names => true)
json.length.should == 0
json[:sessions].length.should == 0
end
it "just self" do
@ -120,9 +120,9 @@ describe ApiMusicSessionsController do
get :sms_index, {client_id: conn.client_id}
json = JSON.parse(response.body, :symbolize_names => true)
json.length.should == 1
json[0][:approved_rsvps][0][:id].should == user.id
json[0][:approved_rsvps][0][:latency].should be_nil # you don't get scores to self
json[:sessions].length.should == 1
json[:sessions][0][:approved_rsvps][0][:id].should == user.id
json[:sessions][0][:approved_rsvps][0][:full_score].should be_nil # you don't get scores to self
end
it "someone else with no score with self" do
@ -131,9 +131,9 @@ describe ApiMusicSessionsController do
get :sms_index, {client_id: conn.client_id}
json = JSON.parse(response.body, :symbolize_names => true)
json.length.should == 1
json[0][:approved_rsvps][0][:id].should == other.id
json[0][:approved_rsvps][0][:latency].should be_nil # there is no score with 'other '
json[:sessions].length.should == 1
json[:sessions][0][:approved_rsvps][0][:id].should == other.id
json[:sessions][0][:approved_rsvps][0][:full_score].should be_nil # there is no score with 'other '
end
it "scores with invitees and RSVP's" do
@ -150,11 +150,11 @@ describe ApiMusicSessionsController do
get :sms_index, {client_id: conn.client_id}
json = JSON.parse(response.body, :symbolize_names => true)
json.length.should == 1
json[0][:approved_rsvps][0][:id].should == other.id
json[0][:approved_rsvps][0][:latency].should == ((network_score + user.last_jam_audio_latency + other.last_jam_audio_latency) / 2).ceil
json[0][:invitations][0][:receiver_id].should == invitee.id
json[0][:invitations][0][:latency].should == ((network_score + user.last_jam_audio_latency + invitee.last_jam_audio_latency) / 2).ceil
json[:sessions].length.should == 1
json[:sessions][0][:approved_rsvps][0][:id].should == other.id
json[:sessions][0][:approved_rsvps][0][:full_score].should == ((network_score + user.last_jam_audio_latency + other.last_jam_audio_latency)).ceil
json[:sessions][0][:invitations][0][:receiver_id].should == invitee.id
json[:sessions][0][:invitations][0][:full_score].should == ((network_score + user.last_jam_audio_latency + invitee.last_jam_audio_latency)).ceil
end
end

View File

@ -4,7 +4,7 @@ describe ApiUsersController do
render_views
let(:user) { FactoryGirl.create(:user) }
let (:conn) { FactoryGirl.create(:connection, :user => user) }
let (:conn) { FactoryGirl.create(:connection, user: user, last_jam_audio_latency: 5) }
before(:each) do
@ -14,23 +14,34 @@ describe ApiUsersController do
describe "audio_latency" do
it "updates both connection and user" do
post :audio_latency, id: user.id, client_id: conn.client_id, audio_latency: 1.5, :format => 'json'
post :audio_latency, id: user.id, client_id: conn.client_id, audio_latency: 3.5, :format => 'json'
response.should be_success
conn.reload
conn.last_jam_audio_latency.should == 1.5
conn.last_jam_audio_latency.should == 3.5
user.reload
conn.user.last_jam_audio_latency.should == 1.5
conn.user.last_jam_audio_latency.should == 3.5
end
it "if connection does not exist, user is still updated" do
post :audio_latency, id: user.id, client_id: 'nothingness', audio_latency: 1.5, :format => 'json'
post :audio_latency, id: user.id, client_id: 'nothingness', audio_latency: 3.5, :format => 'json'
response.should be_success
user.reload
conn.user.last_jam_audio_latency.should == 1.5
conn.user.last_jam_audio_latency.should == 3.5
end
it "ignores latencies of 2 or less" do
post :audio_latency, id: user.id, client_id: conn.client_id, audio_latency: 2, :format => 'json'
response.should be_success
conn.reload
conn.last_jam_audio_latency.should == 5
user.reload
conn.user.last_jam_audio_latency.should == 5
end
end
end

View File

@ -4,8 +4,12 @@ describe "Find Session", :js => true, :type => :feature, :capybara_feature => tr
subject { page }
let(:user) { FactoryGirl.create(:user) }
let(:finder) { FactoryGirl.create(:user) }
let(:austin) { austin_geoip }
let(:dallas) { dallas_geoip }
let(:user) { FactoryGirl.create(:user, last_jam_locidispid: austin_geoip[:locidispid], last_jam_addr: austin_ip) }
let(:finder) { FactoryGirl.create(:user, last_jam_locidispid: dallas_geoip[:locidispid], last_jam_addr: dallas_ip) }
before(:each) do
UserMailer.deliveries.clear
@ -22,9 +26,9 @@ describe "Find Session", :js => true, :type => :feature, :capybara_feature => tr
describe "basic" do
before(:each) do
create_phony_database
emulate_client
sign_in_poltergeist user
visit "/client#/findSession"
fast_signin(user, "/client#/findSession")
FactoryGirl.create(:friendship, :user => user, :friend => finder)
end
@ -43,7 +47,7 @@ describe "Find Session", :js => true, :type => :feature, :capybara_feature => tr
describe "listing behavior" do
describe "active sessions" do
let!(:session1) { FactoryGirl.create(:single_user_session) }
let!(:session1_creator) { FactoryGirl.create(:single_user_session, last_jam_locidispid: dallas_geoip[:locidispid], last_jam_addr: dallas_ip) }
it "find one active session" do
find('#btn-refresh').trigger(:click)
@ -62,6 +66,35 @@ describe "Find Session", :js => true, :type => :feature, :capybara_feature => tr
# find('#end-of-session-list')
# page.all('div#sessions-active .found-session').count.should == 21
end
it "shows latency information correctly" do
# this will try to show all 6 latency badges. unknown, good, fair, poor, unacceptable, and me
verify_find_session_score(nil, '#sessions-active', user, session1_creator)
verify_find_session_score(3, '#sessions-active', user, session1_creator)
verify_find_session_score(40, '#sessions-active', user, session1_creator)
verify_find_session_score(80, '#sessions-active', user, session1_creator)
verify_find_session_score(110, '#sessions-active', user, session1_creator)
fast_signout
fast_signin(session1_creator, "/client#/findSession")
verify_find_session_score(nil, '#sessions-active', session1_creator, session1_creator)
# this should cause it to move to the scheduled session view; we'll check all the values again
ActiveMusicSession.delete_all
verify_find_session_score(nil, '#sessions-scheduled', session1_creator, session1_creator)
fast_signout
fast_signin(user, "/client#/findSession")
verify_find_session_score(nil, '#sessions-scheduled', user, session1_creator)
verify_find_session_score(3, '#sessions-scheduled', user, session1_creator)
verify_find_session_score(40, '#sessions-scheduled', user, session1_creator)
verify_find_session_score(80, '#sessions-scheduled', user, session1_creator)
verify_find_session_score(110, '#sessions-scheduled', user, session1_creator)
end
end
describe "scheduled sessions" do

View File

@ -4,19 +4,19 @@ describe "Musician Search", :js => true, :type => :feature, :capybara_feature =>
subject { page }
before(:all) do
poltergeist_setup
end
let(:austin) { austin_geoip }
let(:dallas) { dallas_geoip }
let(:user) {FactoryGirl.create(:user, last_jam_locidispid: austin_geoip[:locidispid], last_jam_addr: austin_ip)}
let(:user2) {FactoryGirl.create(:user, last_jam_locidispid: dallas_geoip[:locidispid], last_jam_addr: dallas_ip)}
before(:each) do
@user = FactoryGirl.create(:user, last_jam_locidispid: 1)
@user2 = FactoryGirl.create(:user, last_jam_locidispid: 1)
MaxMindManager.create_phony_database
Score.delete_all
Score.createx(1, 'a', 1, 1, 'a', 1, 10)
Score.createx(austin_geoip[:locidispid], 'a', 1, dallas_geoip[:locidispid], 'a', 1, 10)
ActiveRecord::Base.logger.debug '====================================== begin ======================================'
sign_in_poltergeist @user
sign_in_poltergeist user
visit "/client#/musicians"
end
@ -47,4 +47,15 @@ describe "Musician Search", :js => true, :type => :feature, :capybara_feature =>
expect(page).to_not have_selector('#musician-filter-results .musician-list-result')
end
it "shows latency information correctly" do
# this will try to show 5 latency badges. unknown, good, fair, poor, unacceptable. 'me' does not happen on this screen
user.last_jam_locidispid = austin[:locidispid]
user.save!
verify_find_musician_score(nil, user, user2)
verify_find_musician_score(3, user, user2)
verify_find_musician_score(40, user, user2)
verify_find_musician_score(80, user, user2)
verify_find_musician_score(110, user, user2)
end
end

View File

@ -3,7 +3,9 @@ require 'spec_helper'
describe "Session Detail", :js => true, :type => :feature, :capybara_feature => true do
let(:searcher) { FactoryGirl.create(:user) }
let(:austin) { austin_geoip }
let(:dallas) { dallas_geoip }
let(:searcher) { FactoryGirl.create(:user, last_jam_locidispid: austin_geoip[:locidispid], last_jam_addr: austin_ip) }
let(:requested_rsvp_slot) {FactoryGirl.create(:requested_rsvp_slot, user: searcher)}
before(:each) do
@ -64,4 +66,24 @@ describe "Session Detail", :js => true, :type => :feature, :capybara_feature =>
find('a#btnCancelRsvp').trigger(:click)
should_not have_selector('td', text: searcher.name)
end
it "shows latency information correctly" do
# this will try to show all 6 latency badges. unknown, good, fair, poor, unacceptable, and me
session_creator = requested_rsvp_slot.music_session.creator
session_creator.last_jam_locidispid = dallas_geoip[:locidispid]
session_creator.last_jam_addr = dallas_ip
session_creator.save!
fast_signin(session_creator, Nav.session_detail(requested_rsvp_slot.music_session))
find('h2', text: 'RSVPs')
target_path = Nav.session_detail(requested_rsvp_slot.music_session)
verify_session_detail_score(nil, session_creator, searcher, target_path)
verify_session_detail_score(nil, session_creator, session_creator, target_path)
verify_session_detail_score(3, session_creator, searcher, target_path)
verify_session_detail_score(40, session_creator, searcher, target_path)
verify_session_detail_score(80, session_creator, searcher, target_path)
verify_session_detail_score(110, session_creator, searcher, target_path)
end
end

View File

@ -2,6 +2,10 @@ require 'spec_helper'
describe "Session Info", :js => true, :type => :feature, :capybara_feature => true do
let(:austin) { austin_geoip }
let(:dallas) { dallas_geoip }
before(:each) do
SessionInfoComment.delete_all
Notification.delete_all
@ -11,20 +15,21 @@ describe "Session Info", :js => true, :type => :feature, :capybara_feature => tr
Invitation.delete_all
MusicSession.delete_all
User.delete_all
MaxMindManager.create_phony_database
@rsvp_approved_user = FactoryGirl.create(:user)
@rsvp_approved_user = FactoryGirl.create(:user, last_jam_locidispid: dallas_geoip[:locidispid], last_jam_addr: dallas_ip)
@rsvp_approved_user.save
@rsvp_declined_user = FactoryGirl.create(:user)
@rsvp_declined_user = FactoryGirl.create(:user, last_jam_locidispid: dallas_geoip[:locidispid], last_jam_addr: dallas_ip)
@rsvp_declined_user.save
@session_invitee = FactoryGirl.create(:user)
@session_invitee = FactoryGirl.create(:user, last_jam_locidispid: dallas_geoip[:locidispid], last_jam_addr: dallas_ip)
@session_invitee.save
@non_session_invitee = FactoryGirl.create(:user)
@non_session_invitee = FactoryGirl.create(:user, ;)
@non_session_invitee.save
@session_creator = FactoryGirl.create(:user)
@session_creator = FactoryGirl.create(:user, last_jam_locidispid: austin_geoip[:locidispid], last_jam_addr: austin_ip)
@session_creator.save
# session invitations require sender and receiver to be friends
@ -42,19 +47,13 @@ describe "Session Info", :js => true, :type => :feature, :capybara_feature => tr
@url = "/sessions/#{@music_session.id}/details"
@slot1 = FactoryGirl.create(:rsvp_slot, :music_session => @music_session, :instrument => JamRuby::Instrument.find('electric guitar'))
@slot2 = FactoryGirl.create(:rsvp_slot, :music_session => @music_session, :instrument => JamRuby::Instrument.find('drums'))
@invitation = FactoryGirl.create(:invitation, :sender => @session_creator, :receiver => @session_invitee, :music_session => @music_session)
@invitation = FactoryGirl.create(:invitation, :sender => @session_creator, :receiver => @rsvp_approved_user, :music_session => @music_session)
@invitation = FactoryGirl.create(:invitation, :sender => @session_creator, :receiver => @rsvp_declined_user, :music_session => @music_session)
# create RSVP request 1
@rsvp1 = RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id], :message => "Let's Jam!"}, @rsvp_approved_user)
# create RSVP request 2
@rsvp2 = RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id], :message => "Let's Jam!"}, @rsvp_declined_user)
# approve slot1
@ -205,6 +204,18 @@ describe "Session Info", :js => true, :type => :feature, :capybara_feature => tr
fast_signout
end
it "shows latency information correctly" do
# this will try to show all 6 latency badges. unknown, good, fair, poor, unacceptable, and me
fast_signin(@session_creator, @url)
verify_session_info_score(nil, @session_creator, @session_creator)
verify_session_info_score(nil, @session_creator, @rsvp_approved_user)
verify_session_info_score(3, @session_creator, @rsvp_approved_user)
verify_session_info_score(40, @session_creator, @rsvp_approved_user)
verify_session_info_score(80, @session_creator, @rsvp_approved_user)
verify_session_info_score(110, @session_creator, @rsvp_approved_user)
end
# musician_access = true, approval_required = false
it "should allow anyone to view for 'at will' option after session starts" do
@music_session.musician_access = true

View File

@ -60,15 +60,15 @@ def web_config
end
def max_good_full_score
20
40
end
def max_yellow_full_score
35
70
end
def max_red_full_score
50
100
end
end
klass.new

View File

@ -159,12 +159,20 @@ def create_phony_database
GeoIpBlocks.connection.execute("select generate_scores_dataset()").check
end
def austin_ip
IPAddr.new(0x0FFFFFFF, Socket::AF_INET).to_s
end
def dallas_ip
IPAddr.new(0x1FFFFFFF, Socket::AF_INET).to_s
end
# gets related models for an IP in the 1st block from the scores_better_test_data.sql
def austin_geoip
geoiplocation = GeoIpLocations.find_by_locid(17192)
geoipblock = GeoIpBlocks.find_by_locid(17192)
jamisp = JamIsp.find_by_beginip(geoipblock.beginip)
{jamisp: jamisp, geoiplocation: geoiplocation, geoipblock: geoipblock }
{jamisp: jamisp, geoiplocation: geoiplocation, geoipblock: geoipblock, locidispid: Score.compute_locidispid(geoiplocation.locid, jamisp.coid) }
end
# gets related models for an IP in the 1st block from the scores_better_test_data.sql
@ -172,7 +180,7 @@ def dallas_geoip
geoiplocation = GeoIpLocations.find_by_locid(667)
geoipblock = GeoIpBlocks.find_by_locid(667)
jamisp = JamIsp.find_by_beginip(geoipblock.beginip)
{jamisp: jamisp, geoiplocation: geoiplocation, geoipblock: geoipblock}
{jamisp: jamisp, geoiplocation: geoiplocation, geoipblock: geoipblock, locidispid: Score.compute_locidispid(geoiplocation.locid, jamisp.coid)}
end
# attempts to make the creation of a score more straightforward.
@ -192,4 +200,116 @@ def locidispid_from_ip(ip_address)
else
nil
end
end
def expected_score_info(score, current_user, target_user)
full_score = score ? score + current_user.last_jam_audio_latency + target_user.last_jam_audio_latency : nil
if current_user == target_user
expected = {latency_badge_selector: '.latency-me', latency_badge_text: 'ME', color: nil}
elsif full_score == nil
expected = {latency_badge_selector: '.latency-unknown', latency_badge_text: 'UNKNOWN', color: 'purple'}
elsif full_score < 40
expected = {latency_badge_selector: '.latency-good', latency_badge_text: 'GOOD', color: 'green'}
elsif full_score < 70
expected = {latency_badge_selector: '.latency-fair', latency_badge_text: 'FAIR', color: 'yellow'}
elsif full_score < 100
expected = {latency_badge_selector: '.latency-poor', latency_badge_text: 'POOR', color: 'red'}
else
expected = {latency_badge_selector: '.latency-unacceptable', latency_badge_text: 'UNACCEPTABLE', color: 'blue'}
end
expected
end
# will verify that the score shown match the score desired
def verify_find_session_score(score, parent_selector, current_user, target_user)
expected = expected_score_info(score, current_user, target_user)
Score.connection.execute('DELETE FROM scores').check
if score
create_score(austin, dallas, { auserid: current_user.id, buserid: target_user.id},
austin[:jamisp].beginip, dallas[:jamisp].beginip, 'a_client_id', 'b_client_id', score=score) # creates scores with very recent created_at, so it should be skipped
end
find('#btn-refresh').trigger(:click)
page.assert_selector("div#{parent_selector} .found-session", count: 1)
hoverable = find(".latency-value#{expected[:latency_badge_selector]}[data-user-id='#{target_user.id}']", text: expected[:latency_badge_text])
verify_score_hover(score, current_user, target_user, hoverable)
end
# will verify that the score shown match the score desired
def verify_find_musician_score(score, current_user, target_user)
expected = expected_score_info(score, current_user, target_user)
Score.connection.execute('DELETE FROM scores').check
if score
create_score(austin, dallas, { auserid: current_user.id, buserid: target_user.id},
austin[:jamisp].beginip, dallas[:jamisp].beginip, 'a_client_id', 'b_client_id', score=score) # creates scores with very recent created_at, so it should be skipped
end
visit '/client#/musicians'
hoverable = find(".musician-list-result[data-musician-id='#{target_user.id}'] .score-count#{expected[:latency_badge_selector]} ")
hoverable.find('img')['src'].include?("icon_#{expected[:color]}_score.png").should be_true
verify_score_hover(score, current_user, target_user, hoverable)
end
# will verify that the score shown match the score desired
def verify_session_info_score(score, current_user, target_user)
expected = expected_score_info(score, current_user, target_user)
Score.connection.execute('DELETE FROM scores').check
if score
create_score(austin, dallas, { auserid: current_user.id, buserid: target_user.id},
austin[:jamisp].beginip, dallas[:jamisp].beginip, 'a_client_id', 'b_client_id', score=score) # creates scores with very recent created_at, so it should be skipped
end
visit current_path
hoverable = find(".rsvp-details[data-user-id='#{target_user.id}'] .latency#{expected[:latency_badge_selector]}", text: expected[:latency_badge_text])
verify_score_hover(score, current_user, target_user, hoverable)
end
# will verify that the score shown match the score desired
def verify_session_detail_score(score, current_user, target_user, target_path)
expected = expected_score_info(score, current_user, target_user)
Score.connection.execute('DELETE FROM scores').check
if score
create_score(austin, dallas, { auserid: current_user.id, buserid: target_user.id},
austin[:jamisp].beginip, dallas[:jamisp].beginip, 'a_client_id', 'b_client_id', score=score) # creates scores with very recent created_at, so it should be skipped
end
visit target_path
hoverable = find(".latency#{expected[:latency_badge_selector]}[data-user-id='#{target_user.id}']", text: expected[:latency_badge_text])
verify_score_hover(score, current_user, target_user, hoverable)
end
def verify_score_hover(score, current_user, target_user, hoverable)
hoverable.help_bubble
if current_user == target_user
find('.help-musician-score-self p', text: 'You are looking at your own account.')
if current_user.last_jam_audio_latency
find('.help-musician-score-self .my-gear-latency .measurement-value', text: "#{current_user.last_jam_audio_latency.round} ms")
else
find('.help-musician-score-self .my-gear-latency .measurement-value', text: "13 ms*")
end
else
find('.help-musician-score-count p', text: 'The score shown is the one-way latency (or delay) in milliseconds from you to this user. This score is calculated using the following three values that JamKazam gathers:')
find('.help-musician-score-count .my-gear-latency.partial .measurement-value', text: current_user.last_jam_audio_latency ? "#{current_user.last_jam_audio_latency.round} ms" : '13 ms*')
find('.help-musician-score-count .their-gear-latency.partial .measurement-value', text: target_user.last_jam_audio_latency ? "#{target_user.last_jam_audio_latency.round} ms" : '13 ms*')
find('.help-musician-score-count .internet-latency.partial .measurement-value', text: score ? "#{score} ms": '?')
end
end

View File

@ -2,12 +2,6 @@ module Requests
module FeatureHelpers
def poltergeist_setup
Capybara.javascript_driver = :poltergeist
Capybara.current_driver = Capybara.javascript_driver
Capybara.default_wait_time = 30 # these tests are SLOOOOOW
end
end
module JsonHelpers

View File

@ -18,6 +18,10 @@ module Capybara
sleep 0.3
attempt_hover
end
def help_bubble
hover
end
end
end
end

View File

@ -569,7 +569,7 @@ module JamWebsockets
user = valid_login(username, password, token, client_id)
# protect against this user swamping the server
if Connection.where(user_id: user.id).count >= @max_connections_per_user
if user && Connection.where(user_id: user.id).count >= @max_connections_per_user
@log.warn "user #{user.id}/#{user.email} unable to connect due to max_connections_per_user #{@max_connections_per_user}"
raise SessionError, 'max_user_connections'
end