* VRFS-1684 and VRFS-1686 - updating stored procs, added /api/sessions/nonactive/, adding a bunch of tests for sms_index
This commit is contained in:
parent
5ad6db90ef
commit
106222638f
|
|
@ -175,3 +175,4 @@ audio_latency.sql
|
|||
ams_index.sql
|
||||
update_ams_index.sql
|
||||
update_ams_index_2.sql
|
||||
sms_index.sql
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
-- my_audio_latency can have a special value of -1, which means 'unknown'.
|
||||
CREATE OR REPLACE FUNCTION sms_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 sms_music_session_tmp (music_session_id VARCHAR(64) NOT NULL, tag INTEGER, latency INTEGER) ON COMMIT DROP;
|
||||
|
||||
-- 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 > (NOW() - (interval '15 minute'));
|
||||
|
||||
-- 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.musician_access = TRUE AND
|
||||
q.tag IS NULL;
|
||||
|
||||
-- delete anything not tagged
|
||||
DELETE FROM sms_music_session_tmp WHERE tag IS NULL;
|
||||
|
||||
-- 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, latency 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.score+my_audio_latency+users.last_jam_audio_latency)/2 AS latency
|
||||
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 AND
|
||||
rsvp_requests_rsvp_slots.chosen = TRUE;
|
||||
|
||||
-- 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.score+my_audio_latency+users.last_jam_audio_latency)/2 AS latency
|
||||
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.latency) 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, latency 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.score+my_audio_latency+c.last_jam_audio_latency)/2 AS latency
|
||||
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.score+my_audio_latency+users.last_jam_audio_latency)/2 AS latency
|
||||
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.latency) FROM ams_users_tmp u WHERE
|
||||
q.music_session_id = u.music_session_id);
|
||||
|
||||
RETURN;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
|
@ -320,7 +320,8 @@ module JamRuby
|
|||
|
||||
connection = Connection.where(user_id: current_user.id, client_id: client_id).first!
|
||||
my_locidispid = connection.locidispid
|
||||
my_audio_latency = connection.last_jam_audio_latency || 0 # we can't assume that the user has an audio_latency when searching
|
||||
# 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
|
||||
|
||||
self.connection.execute("select ams_index('#{current_user.id}'::varchar, #{my_locidispid}::bigint, #{my_audio_latency}::integer)");
|
||||
end
|
||||
|
|
@ -329,7 +330,7 @@ module JamRuby
|
|||
# (and tagged) by rsvp'd (1st), invited (2nd), and musician can join (3rd). within a group tagged the
|
||||
# same, sorted by score. date seems irrelevant as these are active sessions. ams_init must be called
|
||||
# first.
|
||||
def self.ams_index(current_user, options = {})
|
||||
def self.ams_query(current_user, options = {})
|
||||
client_id = options[:client_id]
|
||||
genre = options[:genre]
|
||||
lang = options[:lang]
|
||||
|
|
@ -379,11 +380,11 @@ module JamRuby
|
|||
}
|
||||
)
|
||||
|
||||
if (offset)
|
||||
if offset
|
||||
query = query.offset(offset)
|
||||
end
|
||||
|
||||
if (limit)
|
||||
if limit
|
||||
query = query.limit(limit)
|
||||
end
|
||||
|
||||
|
|
@ -421,6 +422,30 @@ module JamRuby
|
|||
.order('ams_users_tmp.music_session_id, ams_users_tmp.user_id')
|
||||
end
|
||||
|
||||
# wrap me in a transaction!
|
||||
# note that these queries must be actualized before the end of the transaction
|
||||
# else the temporary tables created by sms_init will be gone.
|
||||
def self.ams_index(current_user, params)
|
||||
ActiveMusicSession.ams_init(current_user, params)
|
||||
|
||||
music_sessions = ActiveMusicSession.ams_query(current_user,
|
||||
client_id: params[:client_id],
|
||||
genre: params[:genre],
|
||||
lang: params[:lang],
|
||||
keyword: params[:keyword],
|
||||
offset: params[:offset],
|
||||
limit: params[:limit]).all
|
||||
|
||||
music_session_users = ActiveMusicSession.ams_users.all
|
||||
|
||||
user_scores = {}
|
||||
music_session_users.each do |user|
|
||||
user_scores[user.id] = {latency: user.latency}
|
||||
end
|
||||
|
||||
[music_sessions, user_scores]
|
||||
end
|
||||
|
||||
def self.participant_create user, music_session_id, client_id, as_musician, tracks
|
||||
music_session = MusicSession.find(music_session_id)
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ module JamRuby
|
|||
private
|
||||
|
||||
def require_sender_in_music_session
|
||||
unless music_session.part_of_session? sender
|
||||
if music_session && !music_session.part_of_session?(sender)
|
||||
errors.add(:music_session, MEMBERSHIP_REQUIRED_OF_MUSIC_SESSION)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -567,8 +567,141 @@ module JamRuby
|
|||
a.nil? ? nil : a.to_i
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# 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]
|
||||
|
||||
connection = Connection.where(user_id: current_user.id, client_id: client_id).first!
|
||||
my_locidispid = connection.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
|
||||
|
||||
self.connection.execute("select sms_index('#{current_user.id}'::varchar, #{my_locidispid}::bigint, #{my_audio_latency}::integer)");
|
||||
end
|
||||
|
||||
# Generate a list of music sessions (that are active) filtered by genre, language, keyword, and sorted
|
||||
# (and tagged) by rsvp'd (1st), invited (2nd), and musician can join (3rd). within a group tagged the
|
||||
# same, sorted by score. date seems irrelevant as these are active sessions. sms_init must be called
|
||||
# first.
|
||||
def self.sms_query(current_user, options = {})
|
||||
client_id = options[:client_id]
|
||||
genre = options[:genre]
|
||||
lang = options[:lang]
|
||||
keyword = options[:keyword]
|
||||
offset = options[:offset]
|
||||
limit = options[:limit]
|
||||
|
||||
connection = Connection.where(user_id: current_user.id, client_id: client_id).first!
|
||||
my_locidispid = connection.locidispid
|
||||
my_audio_latency = connection.last_jam_audio_latency
|
||||
|
||||
query = MusicSession
|
||||
.select('music_sessions.*')
|
||||
|
||||
# this is not really needed when sms_music_session_tmp is joined
|
||||
# unless there is something specific we need out of active_music_sessions
|
||||
# query = query.joins(
|
||||
# %Q{
|
||||
# INNER JOIN
|
||||
# active_music_sessions
|
||||
# ON
|
||||
# active_music_sessions.id = music_sessions.id
|
||||
# }
|
||||
# )
|
||||
# .select('1::integer as tag, 15::integer as latency')
|
||||
|
||||
# integrate sms_music_session_tmp into the processing
|
||||
# then we can join sms_music_session_tmp and not join active_music_sessions
|
||||
query = query.joins(
|
||||
%Q{
|
||||
INNER JOIN
|
||||
sms_music_session_tmp
|
||||
ON
|
||||
sms_music_session_tmp.music_session_id = music_sessions.id
|
||||
}
|
||||
)
|
||||
.select('sms_music_session_tmp.tag, sms_music_session_tmp.latency')
|
||||
|
||||
query = query.order(
|
||||
%Q{
|
||||
tag, latency, music_sessions.id
|
||||
}
|
||||
)
|
||||
.group(
|
||||
%Q{
|
||||
tag, latency, music_sessions.id
|
||||
}
|
||||
)
|
||||
|
||||
if offset
|
||||
query = query.offset(offset)
|
||||
end
|
||||
|
||||
if limit
|
||||
query = query.limit(limit)
|
||||
end
|
||||
|
||||
# cleanse keyword so it is only word characters. ignore if less than 3 characters long or greater than 100
|
||||
# TODO do we want to force match of whole words only? this matches any substring...
|
||||
# TODO do we want to match more than one word in the phrase? this matches only the first word...
|
||||
|
||||
unless keyword.nil? or keyword.length < 3 or keyword.length > 100
|
||||
w = keyword.split(/\W+/)
|
||||
if w.length > 0 and w[0].length >= 3
|
||||
query = query.where("music_sessions.description like '%#{w[0]}%'")
|
||||
end
|
||||
end
|
||||
|
||||
query = query.where("music_sessions.genre_id = ?", genre) unless genre.nil?
|
||||
|
||||
# TODO filter by lang
|
||||
|
||||
return query
|
||||
end
|
||||
|
||||
# returns the set of users in a music_sessions and the music_session they are in and their latency.
|
||||
# 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')
|
||||
.joins(
|
||||
%Q{
|
||||
INNER JOIN
|
||||
sms_users_tmp
|
||||
ON
|
||||
sms_users_tmp.user_id = users.id
|
||||
}
|
||||
)
|
||||
.order('sms_users_tmp.music_session_id, sms_users_tmp.user_id')
|
||||
end
|
||||
|
||||
# wrap me in a transaction!
|
||||
# note that these queries must be actualized before the end of the transaction
|
||||
# else the temporary tables created by sms_init will be gone.
|
||||
def self.sms_index(current_user, params)
|
||||
MusicSession.sms_init(current_user, params)
|
||||
|
||||
music_sessions = MusicSession.sms_query(current_user,
|
||||
client_id: params[:client_id],
|
||||
genre: params[:genre],
|
||||
lang: params[:lang],
|
||||
keyword: params[:keyword],
|
||||
offset: params[:offset],
|
||||
limit: params[:limit]).all
|
||||
|
||||
music_session_users = MusicSession.sms_users.all
|
||||
|
||||
user_scores = {}
|
||||
music_session_users.each do |user|
|
||||
user_scores[user.id] = {latency: user.latency}
|
||||
end
|
||||
|
||||
[music_sessions, user_scores]
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
def generate_share_token
|
||||
|
||||
token = loop do
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ FactoryGirl.define do
|
|||
country "US"
|
||||
musician true
|
||||
terms_of_service true
|
||||
|
||||
last_jam_audio_latency 5
|
||||
|
||||
#u.association :musician_instrument, factory: :musician_instrument, user: u
|
||||
|
||||
|
|
@ -94,10 +94,15 @@ FactoryGirl.define do
|
|||
genre JamRuby::Genre.first
|
||||
association :creator, :factory => :user
|
||||
open_rsvps false
|
||||
scheduled_start Time.now
|
||||
|
||||
factory :recurring_music_session_weekly do
|
||||
recurring_mode 'weekly'
|
||||
end
|
||||
|
||||
after(:create) do |music_session|
|
||||
FactoryGirl.create(:chosen_rsvp_slot, user: music_session.creator, music_session: music_session)
|
||||
end
|
||||
end
|
||||
|
||||
factory :music_session_user_history, :class => JamRuby::MusicSessionUserHistory do
|
||||
|
|
@ -477,7 +482,19 @@ FactoryGirl.define do
|
|||
end
|
||||
|
||||
factory :rsvp_slot, class: JamRuby::RsvpSlot do
|
||||
|
||||
proficiency_level 'beginner'
|
||||
|
||||
factory :chosen_rsvp_slot do
|
||||
ignore do
|
||||
user nil
|
||||
end
|
||||
|
||||
after(:create) { |rsvp_slot, evaluator|
|
||||
rsvp_request = FactoryGirl.create(:rsvp_request, user: evaluator.user)
|
||||
rsvp_request_rsvp_slot = FactoryGirl.create(:rsvp_request_rsvp_slot, chosen:true, rsvp_request: rsvp_request, rsvp_slot:rsvp_slot)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
factory :rsvp_request, class: JamRuby::RsvpRequest do
|
||||
|
|
@ -485,6 +502,10 @@ FactoryGirl.define do
|
|||
cancel_all false
|
||||
end
|
||||
|
||||
factory :rsvp_request_rsvp_slot, class: JamRuby::RsvpRequestRsvpSlot do
|
||||
chosen false
|
||||
end
|
||||
|
||||
factory :latency_tester, :class => JamRuby::LatencyTester do
|
||||
ignore do
|
||||
connection nil
|
||||
|
|
|
|||
|
|
@ -356,7 +356,7 @@ describe ActiveMusicSession do
|
|||
|
||||
ActiveMusicSession.ams_init(user, client_id: c3.client_id)
|
||||
|
||||
music_sessions = ActiveMusicSession.ams_index(user, client_id: c3.client_id).take(100)
|
||||
music_sessions = ActiveMusicSession.ams_query(user, client_id: c3.client_id).take(100)
|
||||
music_sessions.should_not be_nil
|
||||
music_sessions.length.should == 2
|
||||
music_sessions[0].tag.should_not be_nil
|
||||
|
|
@ -364,7 +364,7 @@ describe ActiveMusicSession do
|
|||
music_sessions[1].tag.should_not be_nil
|
||||
music_sessions[1].latency.should_not be_nil
|
||||
|
||||
users = ActiveMusicSession.ams_users().take(100)
|
||||
users = ActiveMusicSession.ams_users.take(100)
|
||||
users.should_not be_nil
|
||||
users.length.should == 2
|
||||
if users[0].music_session_id == earlier_session.id
|
||||
|
|
|
|||
|
|
@ -208,5 +208,185 @@ describe MusicSession do
|
|||
@music_session.get_connection_ids(exclude_client_id: @connection2.client_id, as_musician: true).should == [@connection1.client_id]
|
||||
end
|
||||
end
|
||||
|
||||
def sms(user, params)
|
||||
ActiveRecord::Base.transaction do
|
||||
return MusicSession.sms_index(user, params)
|
||||
end
|
||||
end
|
||||
|
||||
describe "sms_index" do
|
||||
|
||||
describe "simple" 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)
|
||||
end
|
||||
|
||||
it "no results" do
|
||||
music_sessions, user_scores = sms(searcher, default_opts)
|
||||
music_sessions.length.should == 0
|
||||
user_scores.length.should == 0
|
||||
end
|
||||
|
||||
it "one session, one RSVP (creator)" do
|
||||
creator.last_jam_locidispid = conn.locidispid
|
||||
creator.save!
|
||||
|
||||
music_session = FactoryGirl.create(:music_session, creator: creator)
|
||||
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
|
||||
user_scores.length.should == 1
|
||||
user_scores[creator.id][:latency].should == (network_score + searcher.last_jam_audio_latency + creator.last_jam_audio_latency ) / 2
|
||||
end
|
||||
|
||||
it "filters sessions in the past" do
|
||||
|
||||
music_session = FactoryGirl.create(:music_session, creator: creator)
|
||||
music_sessions, user_scores = sms(searcher, default_opts)
|
||||
music_sessions.length.should == 1
|
||||
|
||||
# 15 minutes is the edge of forgiveness
|
||||
music_session.scheduled_start = 16.minutes.ago
|
||||
music_session.save!
|
||||
|
||||
music_sessions, user_scores = sms(searcher, default_opts)
|
||||
music_sessions.length.should == 0
|
||||
|
||||
# this should still fall in time
|
||||
music_session.scheduled_start = 14.minutes.ago
|
||||
music_session.save!
|
||||
|
||||
music_sessions, user_scores = sms(searcher, default_opts)
|
||||
music_sessions.length.should == 1
|
||||
end
|
||||
|
||||
it "one session, one RSVP (creator), one invitation" do
|
||||
creator.last_jam_locidispid = conn.locidispid
|
||||
creator.save!
|
||||
|
||||
# create an invitee, and friend them with the creator (you have to be friends to send an invite)
|
||||
invitee = FactoryGirl.create(:user, last_jam_audio_latency: 30, last_jam_locidispid: 1)
|
||||
FactoryGirl.create(:friendship, user: creator, friend: invitee)
|
||||
FactoryGirl.create(:friendship, user: invitee, friend: creator)
|
||||
music_session = FactoryGirl.create(:music_session, creator: creator)
|
||||
FactoryGirl.create(:invitation, receiver:invitee, sender:creator, music_session: music_session)
|
||||
|
||||
# create a score between invitee, and searcher
|
||||
Score.createx(invitee.last_jam_locidispid, 'immaterial', 1, searcher_conn.locidispid, searcher_conn.client_id, searcher_conn.addr, network_score, nil)
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
#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
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe "sorting" do
|
||||
let!(:creator_1) { FactoryGirl.create(:user, last_jam_locidispid: 4, last_jam_audio_latency: 8) }
|
||||
let!(:creator_conn_1) { FactoryGirl.create(:connection, user: creator_1, ip_address: '4.4.4.4', locidispid: 4, addr:4) }
|
||||
let!(:creator_2) { FactoryGirl.create(:user, last_jam_locidispid: 1, last_jam_audio_latency: 10) }
|
||||
let!(:creator_conn_2) { FactoryGirl.create(:connection, user: creator_2, ip_address: '4.4.4.4', locidispid: 1, addr:1) }
|
||||
let!(:creator_3) { FactoryGirl.create(:user, last_jam_locidispid: 2, last_jam_audio_latency: 12) }
|
||||
let!(:creator_conn_3) { FactoryGirl.create(:connection, user: creator_3, ip_address: '5.5.5.5', locidispid: 2, addr:2) }
|
||||
let!(:searcher_1) { FactoryGirl.create(:user, last_jam_locidispid: 5, last_jam_audio_latency: 6) }
|
||||
let!(:searcher_conn_1) { FactoryGirl.create(:connection, user: searcher_1, ip_address: '8.8.8.8', locidispid: 5, addr:5) }
|
||||
let!(:searcher_2) { FactoryGirl.create(:user, last_jam_locidispid: 3, last_jam_audio_latency: 14) }
|
||||
let!(:searcher_conn_2) { FactoryGirl.create(:connection, user: searcher_2, ip_address: '9.9.9.9', locidispid: 3, addr:3) }
|
||||
|
||||
let!(:music_session_1) { FactoryGirl.create(:music_session, creator: creator_1) }
|
||||
let!(:music_session_2) { FactoryGirl.create(:music_session, creator: creator_2) }
|
||||
let!(:music_session_3) { FactoryGirl.create(:music_session, creator: creator_3) }
|
||||
|
||||
let(:good_network_score) { 20 }
|
||||
let(:fair_network_score) { 30 }
|
||||
let(:bad_network_score) { 40 }
|
||||
|
||||
before(:each) do
|
||||
|
||||
# add an RSVP for searcher_1 to music_session_1
|
||||
searcher_rsvp_slot = FactoryGirl.create(:rsvp_slot, music_session: music_session_1, instrument: Instrument.find('piano'))
|
||||
searcher_rsvp_request = FactoryGirl.create(:rsvp_request, user: searcher_1)
|
||||
searcher_rsvp_request_rsvp_slot = FactoryGirl.create(:rsvp_request_rsvp_slot, chosen:true, rsvp_request: searcher_rsvp_request, rsvp_slot: searcher_rsvp_slot)
|
||||
|
||||
# add an invitation to searcher_1 to music_session_2
|
||||
FactoryGirl.create(:friendship, user: creator_2, friend: searcher_1)
|
||||
FactoryGirl.create(:friendship, user: searcher_1, friend: creator_2)
|
||||
FactoryGirl.create(:invitation, receiver:searcher_1, sender:creator_2, music_session: music_session_2)
|
||||
|
||||
end
|
||||
|
||||
it "searcher_1" do
|
||||
# create a bad score between searcher_1 and creator_1 (but we should still see it sort 1st because it's got an RSVP to the searcher)
|
||||
Score.createx(searcher_conn_1.locidispid, searcher_conn_1.client_id, searcher_conn_1.addr, creator_conn_1.locidispid, creator_conn_1.client_id, creator_conn_1.addr, bad_network_score, nil)
|
||||
|
||||
# create a fair score between searcher_1 and creator_2 (but we should still see it sort 2st because it's got an invitation to the searcher)
|
||||
Score.createx(searcher_conn_1.locidispid, searcher_conn_1.client_id, searcher_conn_1.addr, creator_conn_2.locidispid, creator_conn_2.client_id, creator_conn_2.addr, fair_network_score, nil)
|
||||
|
||||
# create a good score between searcher_1 and creator_3 (but we should still see it sort last because it's an open session; no affiliation with the searcher)
|
||||
Score.createx(searcher_conn_1.locidispid, searcher_conn_1.client_id, searcher_conn_1.addr, creator_conn_3.locidispid, creator_conn_3.client_id, creator_conn_3.addr, good_network_score, nil)
|
||||
|
||||
music_sessions, user_scores = sms(searcher_1, {client_id: searcher_conn_1.client_id})
|
||||
|
||||
music_sessions.length.should == 3
|
||||
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 = 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 = 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
|
||||
|
||||
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
|
||||
|
||||
# let's make music_session_3 invisible, and verify the count goes to 2
|
||||
music_session_3.musician_access = false
|
||||
music_session_3.save!
|
||||
music_sessions, user_scores = sms(searcher_1, {client_id: searcher_conn_1.client_id})
|
||||
music_sessions.length.should == 2
|
||||
|
||||
# let's make music_session_2 invisible, but still the count should be the same (because searcher_1 have an invite)
|
||||
music_session_2.musician_access = false
|
||||
music_session_2.save!
|
||||
music_sessions, user_scores = sms(searcher_1, {client_id: searcher_conn_1.client_id})
|
||||
music_sessions.length.should == 2
|
||||
|
||||
# and lastly with music_session_1, make it invisible, and still it should be visible to the searcher (because searcher_1 has an invite)
|
||||
music_session_1.musician_access = false
|
||||
music_session_1.save!
|
||||
music_sessions, user_scores = sms(searcher_1, {client_id: searcher_conn_1.client_id})
|
||||
music_sessions.length.should == 2
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -60,38 +60,31 @@ class ApiMusicSessionsController < ApiController
|
|||
limit: params[:limit])
|
||||
end
|
||||
|
||||
def ams_index
|
||||
def ams_index
|
||||
# returns a relation which will produce a list of music_sessions which are active and augmented with attributes
|
||||
# tag and latency, then sorted by tag, latency, and finally music_sessions.id (for stability). the list is
|
||||
# filtered by genre, lang, and keyword, then paged by offset and limit (those are record numbers not page numbers).
|
||||
# tag is 1 for chosen rsvp'd sessions, 2 for invited sessions, 3 for all others (musician_access). if you're the
|
||||
# creator of a session it will be treated the same as if you had rsvp'd and been accepted.
|
||||
|
||||
# tag is 1 for chosen rsvp'd sessions, 2 for invited sessions, 3 for all others (musician_access).
|
||||
# TODO: if you're the creator of a session it will be treated the same as if you had rsvp'd and been accepted.
|
||||
ActiveRecord::Base.transaction do
|
||||
ActiveMusicSession.ams_init(current_user, client_id: params[:client_id])
|
||||
|
||||
# note that these queries must be actualized before the end of the transaction
|
||||
# else the temporary tables created by ams_init will be gone. it would be better
|
||||
# to have the caller wrap the whole thing in a transaction and then remove the
|
||||
# .all calls here.
|
||||
|
||||
@music_sessions = ActiveMusicSession.ams_index(current_user,
|
||||
client_id: params[:client_id],
|
||||
genre: params[:genre],
|
||||
lang: params[:lang],
|
||||
keyword: params[:keyword],
|
||||
offset: params[:offset],
|
||||
limit: params[:limit]).all
|
||||
|
||||
music_session_users = ActiveMusicSession.ams_users.all
|
||||
|
||||
@user_scores = {}
|
||||
music_session_users.each do |user|
|
||||
@user_scores[user.id] = {latency: user.latency}
|
||||
ActiveRecord::Base.transaction do
|
||||
@music_sessions, @user_scores = ActiveMusicSession.ams_index(current_user, params)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def sms_index
|
||||
# returns a relation which will produce a list of music_sessions which are scheduled and augmented with attributes
|
||||
# tag and latency, then sorted by tag, latency, and finally music_sessions.id (for stability). the list is
|
||||
# filtered by genre, lang, and keyword, then paged by offset and limit (those are record numbers not page numbers).
|
||||
# tag is 1 for chosen rsvp'd sessions, 2 for invited sessions, 3 for all others (musician_access).
|
||||
# TODO: if you're the creator of a session it will be treated the same as if you had rsvp'd and been accepted.
|
||||
ActiveRecord::Base.transaction do
|
||||
@music_sessions, @user_scores = MusicSession.sms_index(current_user, params)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def scheduled
|
||||
@music_sessions = MusicSession.scheduled(current_user)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -72,6 +72,10 @@ else
|
|||
|
||||
child({:invitations => :invitations}) {
|
||||
attributes :id, :sender_id, :receiver_id
|
||||
|
||||
node do |invitation|
|
||||
{ latency: user_score(invitation.receiver.id) }
|
||||
end
|
||||
}
|
||||
|
||||
child({:approved_rsvps => :approved_rsvps}) {
|
||||
|
|
@ -83,7 +87,6 @@ else
|
|||
|
||||
}
|
||||
|
||||
|
||||
child(:active_music_session => :active_music_session) {
|
||||
attributes :claimed_recording_initiator_id, :track_changes_counter
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
object @music_sessions
|
||||
|
||||
extends "api_music_sessions/show_history"
|
||||
|
|
@ -154,6 +154,7 @@ SampleApp::Application.routes.draw do
|
|||
match '/sessions/scheduled' => 'api_music_sessions#scheduled', :via => :get
|
||||
match '/sessions/legacy' => 'api_music_sessions#create_legacy', :via => :post
|
||||
match '/sessions/active' => 'api_music_sessions#ams_index', :via => :get
|
||||
match '/sessions/nonactive' => 'api_music_sessions#sms_index', :via => :get
|
||||
match '/sessions/:id' => 'api_music_sessions#show', :via => :get, :as => 'api_session_detail'
|
||||
match '/sessions/:id' => 'api_music_sessions#update', :via => :put
|
||||
match '/sessions/:id' => 'api_music_sessions#session_update', :via => :post
|
||||
|
|
|
|||
|
|
@ -4,19 +4,19 @@ describe ApiMusicSessionsController do
|
|||
render_views
|
||||
|
||||
let(:tracks) { [{'sound' => 'mono', 'client_track_id' => 'abc', 'instrument_id' => 'piano'}] }
|
||||
let(:user) { FactoryGirl.create(:user) }
|
||||
let (:conn) { FactoryGirl.create(:connection, :user => user) }
|
||||
|
||||
let(:other) { FactoryGirl.create(:user) }
|
||||
let (:other_conn) { FactoryGirl.create(:connection, user: other, ip_address: '2.2.2.2') }
|
||||
|
||||
let(:user) { FactoryGirl.create(:user, last_jam_locidispid: 1) }
|
||||
let(:conn) { FactoryGirl.create(:connection, :user => user, ip_address: '1.1.1.1', locidispid:1, addr:1) }
|
||||
let(:other) { FactoryGirl.create(:user, last_jam_locidispid: 2) }
|
||||
let(:other_conn) { FactoryGirl.create(:connection, user: other, ip_address: '2.2.2.2', locidispid:2, addr:2) }
|
||||
let(:third_user) { FactoryGirl.create(:user) }
|
||||
let(:network_score) { 20 }
|
||||
|
||||
before(:each) do
|
||||
controller.current_user = user
|
||||
|
||||
ActiveMusicSession.delete_all
|
||||
MusicSession.delete_all
|
||||
Score.delete_all
|
||||
end
|
||||
|
||||
describe "ams_index" do
|
||||
|
|
@ -57,7 +57,7 @@ describe ApiMusicSessionsController do
|
|||
ams = FactoryGirl.create(:active_music_session, creator: other)
|
||||
other_conn.join_the_session(ams.music_session, true, tracks, other, 10)
|
||||
other_conn.errors.any?.should be_false
|
||||
Score.createx(conn.locidispid, conn.client_id, conn.addr, other_conn.locidispid, other_conn.client_id, other_conn.addr, 20, nil)
|
||||
Score.createx(conn.locidispid, conn.client_id, conn.addr, other_conn.locidispid, other_conn.client_id, other_conn.addr, network_score, nil)
|
||||
|
||||
get :ams_index, {client_id: conn.client_id}
|
||||
json = JSON.parse(response.body, :symbolize_names => true)
|
||||
|
|
@ -75,8 +75,9 @@ describe ApiMusicSessionsController do
|
|||
third_user.last_jam_audio_latency = 10 # RSVP's are an 'offline' search, meaning they use user.last_jam_audio_latency instead of connection.last_jam_audio_latency
|
||||
third_user.last_jam_locidispid = conn.locidispid
|
||||
third_user.save!
|
||||
Score.createx(conn.locidispid, conn.client_id, conn.addr, other_conn.locidispid, other_conn.client_id, other_conn.addr, 20, nil)
|
||||
Score.createx(conn.locidispid, conn.client_id, conn.addr, other_conn.locidispid, other_conn.client_id, other_conn.addr, network_score, nil)
|
||||
|
||||
# set up a second RSVP (other than the creators, pointing to the third_user)
|
||||
rsvp_slot = FactoryGirl.create(:rsvp_slot, music_session: ams.music_session, instrument: Instrument.find('piano'))
|
||||
rsvp_request = FactoryGirl.create(:rsvp_request, user: third_user)
|
||||
rsvp_request_rsvp_slot = FactoryGirl.create(:rsvp_request_rsvp_slot, chosen:true, rsvp_request: rsvp_request, rsvp_slot:rsvp_slot)
|
||||
|
|
@ -85,8 +86,76 @@ describe ApiMusicSessionsController do
|
|||
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][0][:id].should == third_user.id
|
||||
json[0][:approved_rsvps][0][:latency].should == 17.5.ceil # ( 20 (network score) + 10 (third_user.last_jam_audio_latency)+ 5 (user.last_jam_audio_latency) ) / 2
|
||||
json[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]
|
||||
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[: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
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe "sms_index" do
|
||||
|
||||
it "no results" 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
|
||||
end
|
||||
|
||||
it "just self" do
|
||||
# create a session with self in it
|
||||
sms = FactoryGirl.create(:music_session, creator: user)
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
it "someone else with no score with self" do
|
||||
# create a session with someone else in it, but no score
|
||||
sms = FactoryGirl.create(:music_session, creator: other)
|
||||
|
||||
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 '
|
||||
end
|
||||
|
||||
it "scores with invitees and RSVP's" do
|
||||
# create a session with someone else in it, but no score
|
||||
sms = FactoryGirl.create(:music_session, creator: other)
|
||||
Score.createx(conn.locidispid, conn.client_id, conn.addr, other_conn.locidispid, other_conn.client_id, other_conn.addr, network_score, nil)
|
||||
|
||||
invitee = FactoryGirl.create(:user, last_jam_audio_latency: 30, last_jam_locidispid: 3)
|
||||
FactoryGirl.create(:friendship, user: other, friend: invitee)
|
||||
FactoryGirl.create(:friendship, user: invitee, friend: other)
|
||||
FactoryGirl.create(:invitation, sender:other, receiver:invitee, music_session: sms)
|
||||
|
||||
Score.createx(invitee.last_jam_locidispid, 'immaterial', 1, conn.locidispid, conn.client_id, conn.addr, network_score, nil)
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -111,10 +111,16 @@ FactoryGirl.define do
|
|||
genre JamRuby::Genre.first
|
||||
association :creator, :factory => :user
|
||||
open_rsvps false
|
||||
scheduled_start Time.now
|
||||
|
||||
|
||||
factory :recurring_music_session_weekly do
|
||||
recurring_mode 'weekly'
|
||||
end
|
||||
|
||||
after(:create) do |music_session|
|
||||
FactoryGirl.create(:chosen_rsvp_slot, user: music_session.creator, music_session: music_session)
|
||||
end
|
||||
end
|
||||
|
||||
factory :music_session_user_history, :class => JamRuby::MusicSessionUserHistory do
|
||||
|
|
@ -475,9 +481,21 @@ FactoryGirl.define do
|
|||
message Faker::Lorem.characters(10)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
factory :rsvp_slot, class: JamRuby::RsvpSlot do
|
||||
|
||||
proficiency_level 'beginner'
|
||||
|
||||
factory :chosen_rsvp_slot do
|
||||
ignore do
|
||||
user nil
|
||||
end
|
||||
|
||||
after(:create) { |rsvp_slot, evaluator|
|
||||
rsvp_request = FactoryGirl.create(:rsvp_request, user: evaluator.user)
|
||||
rsvp_request_rsvp_slot = FactoryGirl.create(:rsvp_request_rsvp_slot, chosen:true, rsvp_request: rsvp_request, rsvp_slot:rsvp_slot)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
factory :rsvp_request, class: JamRuby::RsvpRequest do
|
||||
|
|
|
|||
Loading…
Reference in New Issue