diff --git a/db/manifest b/db/manifest index 0fab4ebe2..7b5ff60fb 100755 --- a/db/manifest +++ b/db/manifest @@ -185,4 +185,6 @@ music_sessions_iso_639_3.sql discardable_claimed_recordings.sql fix_null_scheduled_start.sql fix_use_open_rsvp.sql -allow_unspecified_rsvps.sql \ No newline at end of file +allow_unspecified_rsvps.sql +music_session_cancel_flag.sql +fix_sms_query_cancel_flag.sql \ No newline at end of file diff --git a/db/up/fix_sms_query_cancel_flag.sql b/db/up/fix_sms_query_cancel_flag.sql new file mode 100644 index 000000000..7e3487b89 --- /dev/null +++ b/db/up/fix_sms_query_cancel_flag.sql @@ -0,0 +1,70 @@ +-- 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 IS NULL OR scheduled_start > (NOW() - (interval '15 minute'))) AND canceled = FALSE; + + -- 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; \ No newline at end of file diff --git a/db/up/music_session_cancel_flag.sql b/db/up/music_session_cancel_flag.sql new file mode 100644 index 000000000..26c15a74a --- /dev/null +++ b/db/up/music_session_cancel_flag.sql @@ -0,0 +1 @@ +alter table music_sessions add column canceled boolean default false; \ No newline at end of file diff --git a/ruby/lib/jam_ruby/resque/scheduled/music_session_scheduler.rb b/ruby/lib/jam_ruby/resque/scheduled/music_session_scheduler.rb index 6babdd5ee..0797247eb 100644 --- a/ruby/lib/jam_ruby/resque/scheduled/music_session_scheduler.rb +++ b/ruby/lib/jam_ruby/resque/scheduled/music_session_scheduler.rb @@ -29,11 +29,10 @@ module JamRuby def run # get all weekly sessions that have ended in the last 15 minutes - criteria = "recurring_mode = 'weekly' AND session_removed_at is not null AND next_session_scheduled = false" + criteria = "recurring_mode = 'weekly' AND session_removed_at is not null AND canceled = false AND next_session_scheduled = false" MusicSession.find_each(:conditions => criteria) do |music_session| music_session.copy end end - end end \ No newline at end of file diff --git a/web/app/assets/javascripts/findSession.js b/web/app/assets/javascripts/findSession.js index 4869fe0d6..bb1ed8090 100644 --- a/web/app/assets/javascripts/findSession.js +++ b/web/app/assets/javascripts/findSession.js @@ -15,17 +15,21 @@ var logger = context.JK.logger; var rest = context.JK.Rest(); var sessionList; - var currentQuery = defaultQuery(); - var currentPage = 0; - var LIMIT = 50; - var $next = null; - var $scroller = null; - var $noMoreSessions = null; - function defaultQuery() { - return {offset:currentPage * LIMIT, limit:LIMIT, page:currentPage}; - } - // for unit tests + var LIMIT = 10; + + var $asNext = null; + var $asScroller = null; + var $noMoreActiveSessions = null; + var currentActiveSessionsPage = 0; + var currentActiveSessionsQuery = defaultActiveSessionsQuery(); + + var $ssNext = null; + var $ssScroller = null; + var $noMoreScheduledSessions = null; + var currentScheduledSessionsPage = 0; + var currentScheduledSessionsQuery = defaultScheduledSessionsQuery(); + function getCategoryEnum() { return CATEGORY; } @@ -39,19 +43,22 @@ $selector.append('
') } + function search() { + logger.debug("Searching for sessions..."); + clearResults(); + buildActiveSessionsQuery(); + buildScheduledSessionsQuery(); + loadSessions(); + } + function loadSessions() { addSpinner($('#sessions-active')); addSpinner($('#sessions-scheduled')); // get active sessions - rest.findActiveSessions(currentQuery) + rest.findActiveSessions(currentActiveSessionsQuery) .done(function(sessions) { - $.each(sessions, function(i, session) { - logger.debug('Rendering session ID = ' + session.id); - sessionList.renderActiveSession(session, $(CATEGORY.ACTIVE.id)); - }); - - afterLoadSessions(sessions, $(CATEGORY.ACTIVE.id), $('#no-active-sessions')); + renderActiveSessions(sessions); }) .fail(function(xhr, textStatus, errorMessage) { if (xhr.status === 404) { @@ -66,13 +73,9 @@ }); // get scheduled sessions - rest.findInactiveSessions(currentQuery) + rest.findInactiveSessions(currentScheduledSessionsQuery) .done(function(sessions) { - $.each(sessions, function(i, session) { - logger.debug('Rendering session ID = ' + session.id); - sessionList.renderInactiveSession(session, $(CATEGORY.SCHEDULED.id)); - }); - afterLoadSessions(sessions, $(CATEGORY.SCHEDULED.id), $('#no-scheduled-sessions')); + renderScheduledSessions(sessions); }) .fail(function(xhr, textStatus, errorMessage) { if (xhr.status === 404) { @@ -87,63 +90,211 @@ }); } - function buildQuery() { - currentQuery = defaultQuery(); + /***************** ACTIVE SESSIONS *****************/ + function defaultActiveSessionsQuery() { + return {offset:currentActiveSessionsPage * LIMIT, limit:LIMIT, page:currentActiveSessionsPage}; + } + + function renderActiveSessions(sessions) { + $.each(sessions, function(i, session) { + logger.debug('Rendering session ID = ' + session.id); + sessionList.renderActiveSession(session, $(CATEGORY.ACTIVE.id)); + }); + + afterLoadActiveSessions(sessions); + } + + function buildActiveSessionsQuery() { + currentActiveSessionsQuery = defaultActiveSessionsQuery(); // genre filter var genres = context.JK.GenreSelectorHelper.getSelectedGenres('#find-session-genre'); if (genres !== null && genres.length > 0) { - currentQuery.genre = genres.join(','); + currentActiveSessionsQuery.genre = genres.join(','); } // date filter var date = $('#session-date-filter').val(); if (date !== null && date.length > 0) { - currentQuery.day = context.JK.formatDateYYYYMMDD(date); - currentQuery.timezone_offset = (new Date().getTimezoneOffset()) / 60; + currentActiveSessionsQuery.day = context.JK.formatDateYYYYMMDD(date); + currentActiveSessionsQuery.timezone_offset = (new Date().getTimezoneOffset()) / 60; } // language filter var language = $sessionLanguageFilter.val(); if (language !== null && language.length > 0) { - currentQuery.lang = language; + currentActiveSessionsQuery.lang = language; } // keyword filter var keyword = $('#session-keyword-srch').val(); if (keyword !== null && keyword.length > 0 && keyword !== 'Search by Keyword') { - currentQuery.keyword = $('#session-keyword-srch').val(); + currentActiveSessionsQuery.keyword = $('#session-keyword-srch').val(); } // always set client ID (required on server) - currentQuery.client_id = context.JK.clientId; + currentActiveSessionsQuery.client_id = context.JK.clientId; - console.log("currentQuery = %o", currentQuery); + console.log("currentActiveSessionsQuery = %o", currentActiveSessionsQuery); - return currentQuery; + return currentActiveSessionsQuery; } - function search() { - logger.debug("Searching for sessions..."); - clearResults(); - buildQuery(); - loadSessions(); - } + function afterLoadActiveSessions(sessionList) { - function afterLoadSessions(sessionList, $headerSelector, $noSessionsMsgSelector) { - - if (sessionList.length == 0) { - $headerSelector.hide(); + var $noSessionsMsgSelector = $('#no-active-sessions'); + + if (sessionList.length === 0 && currentActiveSessionsPage === 0) { + $(CATEGORY.ACTIVE.id).hide(); $noSessionsMsgSelector.show(); } else { - $headerSelector.show(); + $(CATEGORY.ACTIVE.id).show(); $noSessionsMsgSelector.hide(); } + if(sessionList.length < LIMIT) { + // if we retrieve less results than asked for, end searching + $asScroller.infinitescroll('pause'); + + if(currentActiveSessionsPage > 0) { + $noMoreActiveSessions.show(); + } + } + else { + currentActiveSessionsPage++; + buildActiveSessionsQuery(); + registerActiveSessionInfiniteScroll(); + } + 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: $('
Loading ...
'), + img: '/assets/shared/spinner.gif' + }, + path: function(page) { + return '/api/sessions/active?' + $.param(buildActiveSessionsQuery()); + } + },function(json, opts) { + renderActiveSessions(json); + }); + } + + /***************** SCHEDULED SESSIONS *****************/ + function defaultScheduledSessionsQuery() { + return {offset:currentScheduledSessionsPage * LIMIT, limit:LIMIT, page:currentScheduledSessionsPage}; + } + + function renderScheduledSessions(sessions) { + $.each(sessions, function(i, session) { + logger.debug('Rendering session ID = ' + session.id); + sessionList.renderInactiveSession(session, $(CATEGORY.SCHEDULED.id)); + }); + afterLoadScheduledSessions(sessions); + } + + function buildScheduledSessionsQuery() { + currentScheduledSessionsQuery = defaultScheduledSessionsQuery(); + + // genre filter + var genres = context.JK.GenreSelectorHelper.getSelectedGenres('#find-session-genre'); + if (genres !== null && genres.length > 0) { + currentScheduledSessionsQuery.genre = genres.join(','); + } + + // date filter + var date = $('#session-date-filter').val(); + if (date !== null && date.length > 0) { + currentScheduledSessionsQuery.day = context.JK.formatDateYYYYMMDD(date); + currentScheduledSessionsQuery.timezone_offset = (new Date().getTimezoneOffset()) / 60; + } + + // language filter + var language = $sessionLanguageFilter.val(); + if (language !== null && language.length > 0) { + currentScheduledSessionsQuery.lang = language; + } + + // keyword filter + var keyword = $('#session-keyword-srch').val(); + if (keyword !== null && keyword.length > 0 && keyword !== 'Search by Keyword') { + currentScheduledSessionsQuery.keyword = $('#session-keyword-srch').val(); + } + + // always set client ID (required on server) + currentScheduledSessionsQuery.client_id = context.JK.clientId; + + console.log("currentScheduledSessionsQuery = %o", currentScheduledSessionsQuery); + + return currentScheduledSessionsQuery; + } + + function afterLoadScheduledSessions(sessionList) { + var $noSessionsMsgSelector = $('#no-scheduled-sessions'); + + if (sessionList.length === 0 && currentScheduledSessionsPage === 0) { + $(CATEGORY.SCHEDULED.id).hide(); + $noSessionsMsgSelector.show(); + } + else { + $(CATEGORY.SCHEDULED.id).show(); + $noSessionsMsgSelector.hide(); + } + + if(sessionList.length < LIMIT) { + // if we retrieve less results than asked for, end searching + $ssScroller.infinitescroll('pause'); + + if(currentScheduledSessionsPage > 0) { + $noMoreScheduledSessions.show(); + } + } + else { + currentScheduledSessionsPage++; + buildScheduledSessionsQuery(); + registerScheduledSessionInfiniteScroll(); + } + + context.JK.GA.trackFindSessions(sessionList.length); + } + + function registerScheduledSessionInfiniteScroll() { + $ssScroller.infinitescroll({ + behavior: 'local', + navSelector: '#sessions-scheduled .btn-next-wrapper', + nextSelector: '#sessions-scheduled .btn-next', + binder: $ssScroller, + dataType: 'json', + appendCallback: false, + debug: true, + prefill: false, + bufferPx:100, + loading: { + msg: $('
Loading ...
'), + img: '/assets/shared/spinner.gif' + }, + path: function(page) { + return '/api/sessions/inactive?' + $.param(buildScheduledSessionsQuery()); + } + },function(json, opts) { + renderScheduledSessions(json); + }); + } + /******************************************************/ + function beforeShow(data) { context.JK.GenreSelectorHelper.render('#find-session-genre', 'Any Genre'); } @@ -156,49 +307,23 @@ } clearResults(); - buildQuery(); + buildActiveSessionsQuery(); + buildScheduledSessionsQuery(); loadSessions(); } function clearResults() { - currentPage = 0; - $noMoreSessions.hide(); - $scroller.infinitescroll('resume'); + currentActiveSessionsPage = 0; + $noMoreActiveSessions.hide(); + $asScroller.infinitescroll('resume'); $('table#sessions-active').children(':not(:first-child)').remove(); + + currentScheduledSessionsPage = 0; + $noMoreScheduledSessions.hide(); + $ssScroller.infinitescroll('resume'); $('table#sessions-scheduled').children(':not(:first-child)').remove(); } - // function registerInfiniteScroll() { - - // if(gon.use_cached_session_scores) { - - // $scroller.infinitescroll({ - - // behavior: 'local', - // navSelector: '#findSession .btn-next-wrapper', - // nextSelector: '#findSession .btn-next', - // binder: $scroller, - // dataType: 'json', - // appendCallback: false, - // debug: true, - // prefill: false, - // bufferPx:100, - // loading: { - // msg: $('
Loading ...
'), - // img: '/assets/shared/spinner.gif' - // }, - // path: function(page) { - // return '/api/sessions/nindex/' + app.clientId + '?' + $.param(buildQuery()); - // } - // },function(json, opts) { - // // Get current page - // //currentPage = opts.state.currPage; - // // Do something with JSON data, create DOM elements, etc .. - // afterLoadScoredSessions(json); - // }); - // } - // } - function events() { $('#session-keyword-srch').focus(function () { @@ -230,9 +355,13 @@ sessionList = new context.JK.SessionList(app); - $next = $('#findSession .btn-next') - $scroller = $('#findSession .content-body-scroller'); - $noMoreSessions = $('#end-of-session-list'); + $asNext = $('#sessions-active .btn-next') + $asScroller = $('#sessions-active .findsession-scroll-container'); + $noMoreActiveSessions = $('#end-of-as-list'); + + $ssNext = $('#sessions-scheduled .btn-next') + $ssScroller = $('#sessions-scheduled .findsession-scroll-container'); + $noMoreScheduledSessions = $('#end-of-ss-list'); $dateFilter.datepicker({ dateFormat: "D d MM yy", diff --git a/web/app/assets/javascripts/sessionList.js b/web/app/assets/javascripts/sessionList.js index 8e2b9c0b1..6d5f6d7a6 100644 --- a/web/app/assets/javascripts/sessionList.js +++ b/web/app/assets/javascripts/sessionList.js @@ -99,7 +99,7 @@ if (showJoinLink) { // wire up the Join Link to the T&Cs dialog - var $parentRow = $('tr[id=' + session.id + ']', tbGroup); + var $parentRow = $('tr[data-session-id=' + session.id + ']', tbGroup); $('.join-link', $parentRow).click(function(evt) { if(!context.JK.guardAgainstBrowser(app)) { return false; @@ -209,10 +209,10 @@ var row = context.JK.fillTemplate($inactiveSessionTemplate.html(), sessionVals); $(tbGroup).append(row); - var $parentRow = $('tr[id=' + session.id + ']', tbGroup); + var $parentRow = $('tr[data-session-id=' + session.id + ']', tbGroup); if (showRsvpLink) { $('.rsvp-msg', $parentRow).hide(); - var $parentRow = $('tr[id=' + session.id + ']', tbGroup); + var $parentRow = $('tr[data-session-id=' + session.id + ']', tbGroup); $('.rsvp-link', $parentRow).click(function(evt) { ui.launchRsvpSubmitDialog(session.id); }); diff --git a/web/app/assets/stylesheets/client/findSession.css.scss b/web/app/assets/stylesheets/client/findSession.css.scss index 15bf8f208..9d07a4d0f 100644 --- a/web/app/assets/stylesheets/client/findSession.css.scss +++ b/web/app/assets/stylesheets/client/findSession.css.scss @@ -13,7 +13,7 @@ } .content-body-scroller { - overflow:auto !important; + } .session-filter { diff --git a/web/app/assets/stylesheets/client/sessionList.css.scss b/web/app/assets/stylesheets/client/sessionList.css.scss index f6bf67216..96468523d 100644 --- a/web/app/assets/stylesheets/client/sessionList.css.scss +++ b/web/app/assets/stylesheets/client/sessionList.css.scss @@ -1,6 +1,11 @@ +div.findsession-scroll-container { + max-height:275px; + overflow-y:scroll; +} + table.findsession-table, table.local-recordings { - margin-top:6px; width:98%; + height:10%; font-size:11px; color:#fff; background-color:#262626; diff --git a/web/app/controllers/api_music_sessions_controller.rb b/web/app/controllers/api_music_sessions_controller.rb index ea8968daf..2ac953eec 100644 --- a/web/app/controllers/api_music_sessions_controller.rb +++ b/web/app/controllers/api_music_sessions_controller.rb @@ -242,8 +242,8 @@ class ApiMusicSessionsController < ApiController music_session = MusicSession.find(params[:id]) if music_session.can_delete? current_user Notification.send_scheduled_session_cancelled music_session - music_session.destroy - + music_session.canceled = true + music_session.save respond_with responder: ApiResponder, :status => 204 else render :json => { :message => ValidationMessages::PERMISSION_VALIDATION_ERROR }, :status => 404 diff --git a/web/app/views/clients/_findSession.html.erb b/web/app/views/clients/_findSession.html.erb index 19ae201ef..6f146559d 100644 --- a/web/app/views/clients/_findSession.html.erb +++ b/web/app/views/clients/_findSession.html.erb @@ -46,7 +46,7 @@ -
+
<%= render :partial => "sessionList", :locals => {:title => "current, active sessions", :category => "sessions-active"} %> @@ -54,6 +54,10 @@
No active public sessions found.
+
+ No more active sessions. +
+ Next
<%= render :partial => "sessionList", :locals => {:title => "future, scheduled sessions", :category => "sessions-scheduled"} %> @@ -61,15 +65,14 @@
No scheduled sessions found.
+
+ No more scheduled sessions. +
+ Next
- Next
- -
- No more sessions. -
@@ -77,7 +80,7 @@