* VRFS-1934/VRFS-1936 - fix scrolling in sessions, VRFS-1938 - no dups in find session page

This commit is contained in:
Seth Call 2014-07-23 15:11:02 -05:00
parent ce3ae06053
commit cc6f3fe974
15 changed files with 204 additions and 94 deletions

View File

@ -193,3 +193,5 @@ next_session_scheduled_default.sql
migrate_old_sessions.sql migrate_old_sessions.sql
max_mind_releases.sql max_mind_releases.sql
score_histories.sql score_histories.sql
update_sms_index.sql
connection_allow_null_locidispid.sql

View File

@ -0,0 +1 @@
ALTER TABLE connections ALTER COLUMN locidispid DROP NOT NULL;

View File

@ -0,0 +1,73 @@
-- 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) 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 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;
-- 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;

View File

@ -325,8 +325,9 @@ module JamRuby
my_locidispid = connection.locidispid my_locidispid = connection.locidispid
# 13 is an average audio gear value we use if they have not qualified any gear # 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 = connection.last_jam_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, #{my_locidispid}::bigint, #{my_audio_latency}::integer)"); self.connection.execute("select ams_index('#{current_user.id}'::varchar, #{locidispid_expr}, #{my_audio_latency}::integer)").check
end end
# Generate a list of music sessions (that are active) filtered by genre, language, keyword, and sorted # Generate a list of music sessions (that are active) filtered by genre, language, keyword, and sorted

View File

@ -242,7 +242,6 @@ module JamRuby
def self.scheduled user def self.scheduled user
query = MusicSession.where("music_sessions.canceled = FALSE") query = MusicSession.where("music_sessions.canceled = FALSE")
query = query.where("music_sessions.started_at IS NULL")
query = query.where("music_sessions.user_id = '#{user.id}'") query = query.where("music_sessions.user_id = '#{user.id}'")
query = query.where("music_sessions.scheduled_start IS NULL OR music_sessions.scheduled_start > NOW() - '12 hour'::INTERVAL") query = query.where("music_sessions.scheduled_start IS NULL OR music_sessions.scheduled_start > NOW() - '12 hour'::INTERVAL")
query = query.order("music_sessions.scheduled_start ASC") query = query.order("music_sessions.scheduled_start ASC")
@ -588,8 +587,9 @@ module JamRuby
my_locidispid = connection.locidispid my_locidispid = connection.locidispid
# 13 is an average audio gear value we use if they have not qualified any gear # 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 = connection.last_jam_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, #{my_locidispid}::bigint, #{my_audio_latency}::integer)"); self.connection.execute("SELECT sms_index('#{current_user.id}'::varchar, #{locidispid_expr}, #{my_audio_latency}::integer)").check
end end
# Generate a list of music sessions (that are active) filtered by genre, language, keyword, and sorted # Generate a list of music sessions (that are active) filtered by genre, language, keyword, and sorted

View File

@ -503,6 +503,14 @@ describe ActiveMusicSession do
music_sessions.length.should == 1 music_sessions.length.should == 1
music_sessions[0].should == music_session_1.music_session music_sessions[0].should == music_session_1.music_session
end end
it "should allow a null locidispid to search" do
searcher_conn_1.locidispid = nil
searcher_conn_1.save!
music_sessions, user_scores = ams(searcher_1, client_id: searcher_conn_1.client_id)
music_sessions.length.should == 2
end
end end
# todo we need more tests: # todo we need more tests:

View File

@ -518,6 +518,30 @@ describe MusicSession do
user_scores.length.should == 1 # the creator, and the invitee 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][:latency].should == ((network_score + invitee.last_jam_audio_latency + creator.last_jam_audio_latency ) / 2).ceil
end end
it "does not show when it goes active" do
# we create a scheduled session--it should return
music_session = FactoryGirl.create(:music_session, creator: creator, scheduled_start: nil)
music_sessions, user_scores = sms(searcher, default_opts)
music_sessions.length.should == 1
# but then make an active session for this scheduled session
ams = FactoryGirl.create(:active_music_session, music_session: music_session, creator: creator, musician_access: true)
music_sessions, user_scores = sms(searcher, default_opts)
music_sessions.length.should == 0
# finally, delete the active session, and see results go back to one
ams.delete
music_sessions, user_scores = sms(searcher, default_opts)
music_sessions.length.should == 1
end
it "should allow a null locidispid to search" do
searcher_conn.locidispid = nil
searcher_conn.save!
music_sessions, user_scores = sms(searcher, default_opts)
music_sessions.length.should == 0
end
end end

View File

@ -9,6 +9,7 @@
SCHEDULED: {index: 1, id: "table#sessions-scheduled"} SCHEDULED: {index: 1, id: "table#sessions-scheduled"}
}; };
var $screen = null;
var $dateFilter = $("#session-date-filter"); var $dateFilter = $("#session-date-filter");
var $sessionLanguageFilter = $('#session-language-filter'); var $sessionLanguageFilter = $('#session-language-filter');
@ -16,15 +17,14 @@
var rest = context.JK.Rest(); var rest = context.JK.Rest();
var sessionList; var sessionList;
var LIMIT = 10; var ACTIVE_SESSIONS_LIMIT = 50;
var SCHEDULED_SESSIONS_LIMIT = 20;
var $asNext = null;
var $asScroller = null;
var currentActiveSessionsPage = 0;
var currentActiveSessionsQuery = defaultActiveSessionsQuery(); var currentActiveSessionsQuery = defaultActiveSessionsQuery();
var $ssNext = null; var $ssNext = null;
var $ssScroller = null; var $ssScroller = null;
var $ssNoMoreEntries = null;
var currentScheduledSessionsPage = 0; var currentScheduledSessionsPage = 0;
var currentScheduledSessionsQuery = defaultScheduledSessionsQuery(); var currentScheduledSessionsQuery = defaultScheduledSessionsQuery();
@ -90,7 +90,7 @@
/***************** ACTIVE SESSIONS *****************/ /***************** ACTIVE SESSIONS *****************/
function defaultActiveSessionsQuery() { function defaultActiveSessionsQuery() {
return {offset:currentActiveSessionsPage * LIMIT, limit:LIMIT, page:currentActiveSessionsPage}; return {offset:0, limit:SCHEDULED_SESSIONS_LIMIT, page:0};
} }
function renderActiveSessions(sessions) { function renderActiveSessions(sessions) {
@ -98,6 +98,7 @@
sessionList.renderActiveSession(session, $(CATEGORY.ACTIVE.id)); sessionList.renderActiveSession(session, $(CATEGORY.ACTIVE.id));
}); });
afterLoadActiveSessions(sessions); afterLoadActiveSessions(sessions);
} }
@ -141,7 +142,7 @@
var $noSessionsMsgSelector = $('#no-active-sessions'); var $noSessionsMsgSelector = $('#no-active-sessions');
if (sessionList.length === 0 && currentActiveSessionsPage === 0) { if (sessionList.length === 0) {
$(CATEGORY.ACTIVE.id).hide(); $(CATEGORY.ACTIVE.id).hide();
$noSessionsMsgSelector.show(); $noSessionsMsgSelector.show();
} }
@ -150,45 +151,15 @@
$noSessionsMsgSelector.hide(); $noSessionsMsgSelector.hide();
} }
if(sessionList.length < LIMIT) { buildActiveSessionsQuery();
// if we retrieve less results than asked for, end searching
$asScroller.infinitescroll('pause');
}
else {
currentActiveSessionsPage++;
buildActiveSessionsQuery();
registerActiveSessionInfiniteScroll();
}
context.JK.GA.trackFindSessions(sessionList.length); context.JK.GA.trackFindSessions(sessionList.length);
} }
function registerActiveSessionInfiniteScroll() {
$asScroller.infinitescroll({
behavior: 'local',
navSelector: '#sessions-active .btn-next-wrapper',
nextSelector: '#sessions-active .btn-next',
binder: $asScroller,
dataType: 'json',
appendCallback: false,
debug: true,
prefill: false,
bufferPx:100,
loading: {
msg: $('<div class="infinite-scroll-loader">Loading ...</div>'),
img: '/assets/shared/spinner.gif'
},
path: function(page) {
return '/api/sessions/active?' + $.param(buildActiveSessionsQuery());
}
},function(json, opts) {
renderActiveSessions(json);
});
}
/***************** SCHEDULED SESSIONS *****************/ /***************** SCHEDULED SESSIONS *****************/
function defaultScheduledSessionsQuery() { function defaultScheduledSessionsQuery() {
return {offset:currentScheduledSessionsPage * LIMIT, limit:LIMIT, page:currentScheduledSessionsPage}; return {offset:currentScheduledSessionsPage * SCHEDULED_SESSIONS_LIMIT, limit:SCHEDULED_SESSIONS_LIMIT, page:currentScheduledSessionsPage};
} }
function renderScheduledSessions(sessions) { function renderScheduledSessions(sessions) {
@ -246,9 +217,11 @@
$noSessionsMsgSelector.hide(); $noSessionsMsgSelector.hide();
} }
if(sessionList.length < LIMIT) { if(sessionList.length < SCHEDULED_SESSIONS_LIMIT) {
// if we retrieve less results than asked for, end searching // if we retrieve less results than asked for, end searching
$ssScroller.infinitescroll('pause'); $ssScroller.infinitescroll('pause');
$ssNoMoreEntries.show();
$('.infinite-scroll-loader').remove();
} }
else { else {
currentScheduledSessionsPage++; currentScheduledSessionsPage++;
@ -301,13 +274,12 @@
} }
function clearResults() { function clearResults() {
currentActiveSessionsPage = 0;
$asScroller.infinitescroll('resume');
$('table#sessions-active').empty(); $('table#sessions-active').empty();
currentScheduledSessionsPage = 0; currentScheduledSessionsPage = 0;
$ssScroller.infinitescroll('resume'); $ssScroller.infinitescroll('resume');
$('table#sessions-scheduled').empty(); $('table#sessions-scheduled').empty();
$ssNoMoreEntries.hide();
} }
function events() { function events() {
@ -336,11 +308,10 @@
sessionList = new context.JK.SessionList(app); sessionList = new context.JK.SessionList(app);
$asNext = $('#sessions-active .btn-next') $screen = $('#findSession');
$asScroller = $('#sessions-active .findsession-scroll-container'); $ssNext = $screen.find('#sessions-scheduled .btn-next')
$ssScroller = $screen.find('.content-body-scroller');
$ssNext = $('#sessions-scheduled .btn-next') $ssNoMoreEntries = $screen.find('#end-of-ss-list')
$ssScroller = $('#sessions-scheduled .findsession-scroll-container');
$dateFilter.datepicker({ $dateFilter.datepicker({
dateFormat: "D d MM yy", dateFormat: "D d MM yy",

View File

@ -21,7 +21,7 @@
height:inherit; height:inherit;
position:relative; position:relative;
display:block; display:block;
overflow:hidden !important; overflow:auto
} }
.session-filter { .session-filter {
@ -61,11 +61,11 @@
height:20px; height:20px;
} }
.session-container {
overflow:auto;
}
.btn-next { .btn-next {
display:none; display:none;
} }
.end-of-list {
margin-top:0;
}
} }

View File

@ -1,9 +1,5 @@
@import "client/common"; @import "client/common";
div.findsession-scroll-container {
max-height:250px;
overflow:auto;
}
table.findsession-table, table.local-recordings { table.findsession-table, table.local-recordings {
width:98%; width:98%;

View File

@ -54,10 +54,6 @@
<div id="no-active-sessions"> <div id="no-active-sessions">
No active public sessions found. No active public sessions found.
</div> </div>
<div id="end-of-as-list" class="end-of-list">
No more active sessions.
</div>
<span class="btn-next-wrapper"><a href="/api/sessions/active?page=1" class="btn-next">Next</a></span>
</div> </div>
<br /> <br />
<div id="sessions-scheduled" class="session-container"> <div id="sessions-scheduled" class="session-container">

View File

@ -1,6 +1,6 @@
<h2><%= title %></h2> <h2><%= title %></h2>
<br/> <br/>
<div class="findsession-scroll-container"> <div class="findsession-container">
<table class="findsession-table" cellspacing="0" cellpadding="0" border="0"> <table class="findsession-table" cellspacing="0" cellpadding="0" border="0">
<!-- header --> <!-- header -->
<tr> <tr>

View File

@ -6,6 +6,32 @@
<link rel="stylesheet" type="text/css" href="css/ie.css" media="screen, projection"/> <link rel="stylesheet" type="text/css" href="css/ie.css" media="screen, projection"/>
<![endif]--> <![endif]-->
<script src="//ajax.googleapis.com/ajax/libs/webfont/1.4.7/webfont.js"></script> <script src="//ajax.googleapis.com/ajax/libs/webfont/1.4.7/webfont.js"></script>
<script> <script>
if (window.WebFont) { if (window.WebFont) {
WebFont.load({ WebFont.load({

View File

@ -82,10 +82,14 @@ FactoryGirl.define do
before(:create) do |session, evaluator| before(:create) do |session, evaluator|
music_session = FactoryGirl.create(:music_session, name: evaluator.name, description: evaluator.description, fan_chat: evaluator.fan_chat, if evaluator.music_session
fan_access: evaluator.fan_access, approval_required: evaluator.approval_required, musician_access: evaluator.musician_access, session.id = evaluator.music_session.id
genre: evaluator.genre, creator: evaluator.creator, band: evaluator.band, language: evaluator.language) else
session.id = music_session.id music_session = FactoryGirl.create(:music_session, name: evaluator.name, description: evaluator.description, fan_chat: evaluator.fan_chat,
fan_access: evaluator.fan_access, approval_required: evaluator.approval_required, musician_access: evaluator.musician_access,
genre: evaluator.genre, creator: evaluator.creator, band: evaluator.band, language: evaluator.language)
session.id = music_session.id
end
end end
factory :active_music_session do factory :active_music_session do

View File

@ -42,38 +42,46 @@ describe "Find Session", :js => true, :type => :feature, :capybara_feature => tr
describe "listing behavior" do describe "listing behavior" do
describe "one slush session" do describe "active sessions" do
before do let!(:session1) { FactoryGirl.create(:single_user_session) }
@session1 = FactoryGirl.create(:single_user_session)
end
it "find general population user" do it "find one active session" do
pending
find('#btn-refresh').trigger(:click) find('#btn-refresh').trigger(:click)
sleep 1 page.assert_selector('div#sessions-active .found-session', count: 1)
find('div#sessions-active')
page.all('div#sessions-active .found-session').count.should == 1
end end
describe "tons of slush sessions" do
before do
20.times do
FactoryGirl.create(:single_user_session)
end
end
it "find many general users" do it "find many active sessions" do
pending 20.times do
find('#btn-refresh').trigger(:click) FactoryGirl.create(:single_user_session)
sleep 1
find('div#sessions-active')
page.all('div#sessions-active .found-session').count.should == 20
# attempt to scroll down--the end of session list should show, and there should now be 21 items
# page.execute_script('jQuery("#findSession .content-body-scroller").scrollTo("100%",100)') #scroll to the bottom of the element
# find('#end-of-session-list')
# page.all('div#sessions-active .found-session').count.should == 21
end end
find('#btn-refresh').trigger(:click)
page.assert_selector('div#sessions-active .found-session', count: 20)
# attempt to scroll down--the end of session list should show, and there should now be 21 items
# page.execute_script('jQuery("#findSession .content-body-scroller").scrollTo("100%",100)') #scroll to the bottom of the element
# find('#end-of-session-list')
# page.all('div#sessions-active .found-session').count.should == 21
end
end
describe "scheduled sessions" do
let!(:scheduled_session) {FactoryGirl.create(:music_session) }
it "find one scheduled session" do
find('#btn-refresh').trigger(:click)
page.assert_selector('div#sessions-scheduled .found-session', count: 1)
end
it "finds many scheduled sessions" do
20.times do
FactoryGirl.create(:music_session)
end
find('#btn-refresh').trigger(:click)
page.assert_selector('div#sessions-scheduled .found-session', count: 20)
page.execute_script('jQuery("#findSession .content-body-scroller").scrollTo("100%",100)') #scroll to the bottom of the element
find('#end-of-ss-list')
page.assert_selector('div#sessions-scheduled .found-session', count: 21)
end end
end end
end end