Merge branch 'develop' into feature/musician_profile_enhancements

This commit is contained in:
Brian Smith 2015-02-02 19:22:33 -05:00
commit 80305c05aa
15 changed files with 292 additions and 86 deletions

View File

@ -245,4 +245,5 @@ text_messages.sql
text_message_migration.sql
user_model_about_changes.sql
performance_samples.sql
user_presences.sql
user_presences.sql
discard_scores_optimized.sql

View File

@ -0,0 +1,89 @@
DROP FUNCTION IF EXISTS discard_scores();
CREATE FUNCTION discard_scores (keep INTEGER) RETURNS VOID AS $$
BEGIN
DELETE FROM scores WHERE score_dt <
(SELECT score_dt FROM scores s WHERE s.alocidispid = scores.alocidispid AND s.blocidispid = scores.blocidispid ORDER BY score_dt DESC LIMIT 1 OFFSET (keep - 1));
RETURN;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION update_current_network_scores(aloc BIGINT, bloc BIGINT) RETURNS VOID
STRICT VOLATILE AS $$
DECLARE
newscore INTEGER;
newscore_dt TIMESTAMP;
newscore_limited BOOL;
sum INTEGER;
kount INTEGER;
r RECORD;
avgscore INTEGER;
maxscore INTEGER;
minscore INTEGER;
BEGIN
-- find the 6 most recent scores
-- (supposedly newscore is the first...)
-- hybrid scheme: compute the average of some recent scores, then limit newscore to be between 4/5 and 6/5 of the average
newscore := NULL;
newscore_dt := NULL;
newscore_limited := FALSE;
sum := 0;
kount := 0;
FOR r IN SELECT score, score_dt FROM scores WHERE alocidispid = aloc AND blocidispid = bloc ORDER BY score_dt DESC LIMIT 6 LOOP
IF newscore IS NULL THEN
newscore := r.score;
newscore_dt := r.score_dt;
ELSE
sum := sum + r.score;
kount := kount + 1;
END IF;
END LOOP;
-- if no scores in query at all, then delete any current entry
IF newscore IS NULL THEN
DELETE FROM current_network_scores WHERE alocidispid = aloc AND blocidispid = bloc;
IF aloc != bloc THEN
DELETE FROM current_network_scores WHERE alocidispid = bloc AND blocidispid = aloc;
END IF;
END IF;
-- if there are scores older than newscore, then use their average to limit the range of newscore
IF kount > 0 THEN
avgscore := sum / kount;
maxscore := avgscore*6/5;
minscore := avgscore*4/5;
-- the score newscore will be inserted as the current value in current_network_scores, but we will limit it
-- to be no greater than 120% of the average and no less than 80% of the average. this will dampen wild
-- swings in the scores.
IF newscore > maxscore THEN
newscore := maxscore;
newscore_limited := TRUE;
ELSEIF newscore < minscore THEN
newscore := minscore;
newscore_limited := TRUE;
END IF;
END IF;
UPDATE current_network_scores SET score = newscore, limited = newscore_limited, score_dt = newscore_dt WHERE alocidispid = aloc AND blocidispid = bloc;
IF NOT FOUND THEN
INSERT INTO current_network_scores (alocidispid, blocidispid, score, limited, score_dt) VALUES (aloc, bloc, newscore, newscore_limited, newscore_dt);
END IF;
IF aloc != bloc THEN
UPDATE current_network_scores SET score = newscore, limited = newscore_limited, score_dt = newscore_dt WHERE alocidispid = bloc AND blocidispid = aloc;
IF NOT FOUND THEN
INSERT INTO current_network_scores (alocidispid, blocidispid, score, limited, score_dt) VALUES (bloc, aloc, newscore, newscore_limited, newscore_dt);
END IF;
END IF;
-- keep the scores table clean, meaning only up to the most 5 recent scores per group & direction (scorer)
DELETE FROM scores WHERE alocidispid = aloc AND blocidispid = bloc AND scorer = 0 AND score_dt <
(SELECT score_dt FROM scores s WHERE s.alocidispid = aloc AND s.blocidispid = bloc AND s.scorer = 0 ORDER BY score_dt DESC LIMIT 1 OFFSET 4);
DELETE FROM scores WHERE alocidispid = bloc AND blocidispid = aloc AND scorer = 1 AND score_dt <
(SELECT score_dt FROM scores s WHERE s.alocidispid = bloc AND s.blocidispid = aloc AND s.scorer = 1 ORDER BY score_dt DESC LIMIT 1 OFFSET 4);
END;
$$ LANGUAGE plpgsql;

View File

@ -42,6 +42,7 @@ require "jam_ruby/resque/resque_hooks"
require "jam_ruby/resque/audiomixer"
require "jam_ruby/resque/quick_mixer"
require "jam_ruby/resque/icecast_config_writer"
require "jam_ruby/resque/stress_job"
require "jam_ruby/resque/scheduled/audiomixer_retry"
require "jam_ruby/resque/scheduled/icecast_config_retry"
require "jam_ruby/resque/scheduled/icecast_source_check"

View File

@ -35,7 +35,6 @@ Resque.before_first_fork do
end
JamRuby::Stats.init(config)
end
# https://devcenter.heroku.com/articles/forked-pg-connections
Resque.before_fork do

View File

@ -0,0 +1,29 @@
require 'resque'
module JamRuby
# this job exists as a way to manually test a bunch of jobs firing at once. It's not a real job.
class StressJob
extend JamRuby::ResqueStats
@queue = :stress_job
@@log = Logging.logger[StressJob]
def self.perform
@@log.debug("STARTING")
100.times do
user = User.first.id
diagnostic = Diagnostic.first.user_id
count = Diagnostic.all.count
end
@@log.debug("ENDING")
end
end
end

View File

@ -522,29 +522,30 @@ describe Score do
it "works" do
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 20, nil)
Score.count.should == 2
Score.connection.execute("SELECT discard_scores()").check
Score.connection.execute("SELECT discard_scores(5)").check
Score.count.should == 2
end
it "discards over 5 items" do
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 20, nil)
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 20, nil)
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 20, nil)
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 20, nil)
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 20, nil)
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 20, nil)
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 20, 6.days.ago)
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 20, 5.days.ago)
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 20, 4.days.ago)
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 20, 3.days.ago)
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 20, 2.days.ago)
Score.count.should == 12
Score.connection.execute("SELECT discard_scores()").check
Score.count.should == 12
Score.count.should == 10
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 26, nil)
Score.connection.execute("UPDATE scores set created_at = TIMESTAMP '#{2.days.ago}' WHERE score = 26").cmdtuples.should == 2
Score.connection.execute("SELECT discard_scores()").check
Score.count.should == 12
Score.connection.execute("SELECT * FROM scores WHERE score = 20").ntuples.should == 12
Score.connection.execute("SELECT * FROM scores WHERE scorer = 0").ntuples.should == 6
Score.connection.execute("SELECT * FROM scores WHERE scorer = 1").ntuples.should == 6
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 20, 1.days.ago)
Score.count.should == 10
# make a score older than all the rest; it should get whacked
Score.createx(LOCA, NODEA, ADDRA, LOCB, NODEB, ADDRB, 26, 7.days.ago)
Score.count.should == 10
Score.connection.execute("SELECT * FROM scores WHERE score = 20").ntuples.should == 10
Score.connection.execute("SELECT * FROM scores WHERE scorer = 0").ntuples.should == 5
Score.connection.execute("SELECT * FROM scores WHERE scorer = 1").ntuples.should == 5
Score.createx(LOCB, NODEB, ADDRB, LOCA, NODEA, ADDRA, 22, nil)
@ -554,18 +555,35 @@ describe Score do
Score.createx(LOCB, NODEB, ADDRB, LOCA, NODEA, ADDRA, 22, nil)
Score.createx(LOCB, NODEB, ADDRB, LOCA, NODEA, ADDRA, 22, nil)
Score.count.should == 24
Score.connection.execute("SELECT discard_scores()").check
Score.count.should == 24
Score.count.should == 20
Score.createx(LOCB, NODEB, ADDRB, LOCA, NODEA, ADDRA, 36, nil)
Score.connection.execute("UPDATE scores set created_at = TIMESTAMP '#{2.days.ago}' WHERE score = 36").cmdtuples.should == 2
Score.connection.execute("SELECT discard_scores()").check
Score.count.should == 24
Score.connection.execute("SELECT * FROM scores WHERE score = 22").ntuples.should == 12
Score.connection.execute("SELECT * FROM scores WHERE score = 22 AND scorer = 0").ntuples.should == 6
Score.connection.execute("SELECT * FROM scores WHERE score = 22 AND scorer = 1").ntuples.should == 6
Score.connection.execute("SELECT * FROM scores WHERE score = 22").ntuples.should == 10
Score.connection.execute("SELECT * FROM scores WHERE score = 20").ntuples.should == 10
Score.connection.execute("SELECT * FROM scores WHERE scorer = 0").ntuples.should == 10
Score.connection.execute("SELECT * FROM scores WHERE scorer = 1").ntuples.should == 10
Score.createx(LOCB, NODEB, ADDRB, LOCA, NODEA, ADDRA, 36, 7.days.ago)
Score.count.should == 20
Score.connection.execute("SELECT * FROM scores WHERE score = 22").ntuples.should == 10
Score.connection.execute("SELECT * FROM scores WHERE score = 20").ntuples.should == 10
Score.connection.execute("SELECT * FROM scores WHERE scorer = 0").ntuples.should == 10
Score.connection.execute("SELECT * FROM scores WHERE scorer = 1").ntuples.should == 10
# let's create scores between a new location, and make sure they don't distrurb the data we have now
Score.createx(LOCC, NODEC, ADDRC, LOCA, NODEA, ADDRA, 10, nil)
Score.count.should == 22
Score.createx(LOCC, NODEC, ADDRC, LOCA, NODEA, ADDRA, 10, nil)
Score.createx(LOCC, NODEC, ADDRC, LOCA, NODEA, ADDRA, 10, nil)
Score.createx(LOCC, NODEC, ADDRC, LOCA, NODEA, ADDRA, 10, nil)
Score.createx(LOCC, NODEC, ADDRC, LOCA, NODEA, ADDRA, 10, nil)
Score.count.should == 30
Score.connection.execute("SELECT * FROM scores WHERE score = 20").ntuples.should == 10
Score.connection.execute("SELECT * FROM scores WHERE score = 22").ntuples.should == 10
Score.connection.execute("SELECT * FROM scores WHERE score = 10").ntuples.should == 10
end
end
end

View File

@ -840,7 +840,8 @@
context._.each(mixers, function(mixer) {
var mediaType = mixer.media_type;
if(mediaType == 'RecordingTrack') {
// mediaType == null is for backwards compat with older clients. Can be removed soon
if(mediaType == null || mediaType == "" || mediaType == 'RecordingTrack') {
recordingTrackMixers.push(mixer)
}
else if(mediaType == 'BackingTrack') {

View File

@ -224,29 +224,11 @@
}
if (showJoinLink) {
// wire up the Join Link to the T&Cs dialog
// wire up the Join Link to the T&Cs dialog
$('.join-link', $parentRow).click(function(evt) {
if(!context.JK.guardAgainstBrowser(app)) {
return false;
}
if (!context.JK.JamServer.connected) {
app.notifyAlert("Not Connected", 'To create or join a session, you must be connected to the server.');
return false;
}
gearUtils.guardAgainstInvalidConfiguration(app)
.fail(function() {
app.notify(
{ title: "Unable to Join Session",
text: "You can only join a session once you have working audio gear and a tested internet connection."
})
})
.done(function(){
sessionUtils.joinSession(session.id);
})
return false;
sessionUtils.ensureValidClient(app, gearUtils, function() {
sessionUtils.joinSession(session.id);
});
});
}
}
@ -368,7 +350,7 @@
$('a.more.rsvps', $parentRow).click(toggleRsvps);
var showRsvpLink = true;
var noLinkText = '';
var sessionLinkText = '';
$('.rsvp-link-text', $parentRow).hide();
function showStartSessionButton(scheduledStart) {
@ -380,8 +362,8 @@
if (session.creator.id === context.JK.currentUserId) {
showRsvpLink = false;
noLinkText = $('<span class="text"><a class="start" style="color: #fc0">Start session now?</a></span>');
noLinkText.find('a').click(function() {
sessionLinkText = $('<span class="text"><a class="start" style="color: #fc0">Start session now?</a></span>');
sessionLinkText.find('a').click(function() {
ui.launchSessionStartDialog(session);
return false;
});
@ -390,18 +372,18 @@
showRsvpLink = false;
if (session.scheduled_start && showStartSessionButton(session.scheduled_start)) {
noLinkText = $('<span class="text"><a class="start" style="color: #fc0">Start session now?</a>&nbsp;|&nbsp;<a class="cancel" style="color: #fc0">Cancel RSVP</a></span>');
noLinkText.find('a.start').click(function() {
sessionLinkText = $('<span class="text"><a class="start" style="color: #fc0">Start session now?</a>&nbsp;|&nbsp;<a class="cancel" style="color: #fc0">Cancel RSVP</a></span>');
sessionLinkText.find('a.start').click(function() {
ui.launchSessionStartDialog(session);
return false;
});
}
else {
noLinkText = $('<span class="text"><a class="cancel" style="color: #fc0">Cancel RSVP</a></span>');
sessionLinkText = $('<span class="text"><a class="cancel" style="color: #fc0">Cancel RSVP</a></span>');
}
// wire cancel link
noLinkText.find('a.cancel').click(function() {
sessionLinkText.find('a.cancel').click(function() {
ui.launchRsvpCancelDialog(session.id, approvedRsvpId)
.one(EVENTS.RSVP_CANCELED, function() {
rest.getSessionHistory(session.id)
@ -419,8 +401,8 @@
showRsvpLink = false;
if (session.scheduled_start && showStartSessionButton(session.scheduled_start)) {
noLinkText = $('<span class="text"><a class="start" style="color: #fc0">Start session now?</a></span>');
noLinkText.find('a').click(function() {
sessionLinkText = $('<span class="text"><a class="start" style="color: #fc0">Start session now?</a></span>');
sessionLinkText.find('a').click(function() {
ui.launchSessionStartDialog(session);
return false;
});
@ -428,8 +410,8 @@
}
else if (pendingRsvpId) {
showRsvpLink = false;
noLinkText = $('<span class="text"><a class="cancel" style="color: #fc0">Cancel RSVP</a></span>');
noLinkText.find('a').click(function() {
sessionLinkText = $('<span class="text"><a class="cancel" style="color: #fc0">Cancel RSVP</a></span>');
sessionLinkText.find('a').click(function() {
ui.launchRsvpCancelDialog(session.id, pendingRsvpId)
.one(EVENTS.RSVP_CANCELED, function() {
rest.getSessionHistory(session.id)
@ -445,11 +427,11 @@
}
else if (!openSlots) {
showRsvpLink = false;
noLinkText = '<span class="text">No more openings in this session.</span>';
sessionLinkText = '<span class="text">No more openings in this session.</span>';
}
else if (!openRsvps && !hasInvitation) {
showRsvpLink = false;
noLinkText = '<span class="text">You need an invitation to RSVP to this session.</span>';
sessionLinkText = '<span class="text">You need an invitation to RSVP to this session.</span>';
}
if (showRsvpLink) {
@ -472,7 +454,7 @@
});
}
else {
$('.rsvp-msg', $parentRow).html(noLinkText).show();
$('.rsvp-msg', $parentRow).html(sessionLinkText).show();
$('.rsvp-link', $parentRow).hide();
}
}

View File

@ -125,7 +125,33 @@
}
}
sessionUtils.ensureValidClient = function(app, gearUtils, successCallback) {
if(!context.JK.guardAgainstBrowser(app)) {
return false;
}
if (!context.JK.JamServer.connected) {
app.notifyAlert("Not Connected", 'To create or join a session, you must be connected to the server.');
return false;
}
gearUtils.guardAgainstInvalidConfiguration(app)
.fail(function() {
app.notify(
{ title: "Unable to Join Session",
text: "You can only join a session once you have working audio gear and a tested internet connection."
});
})
.done(function() {
if (successCallback) {
successCallback();
}
});
}
sessionUtils.joinSession = function(sessionId) {
var hasInvitation = false;
var session = null;
// we need to do a real-time check of the session in case the settings have

View File

@ -6,6 +6,7 @@
context.JK.UIHelper = function(app) {
var logger = context.JK.logger;
var rest = new context.JK.Rest();
var sessionUtils = context.JK.SessionUtils;
function addSessionLike(sessionId, userId, $likeCountSelector, $likeButtonSelector) {
rest.addSessionLike(sessionId, userId)
@ -54,9 +55,11 @@
}
function launchSessionStartDialog(session) {
var sessionStartDialog = new JK.SessionStartDialog(JK.app, session);
sessionStartDialog.initialize();
return sessionStartDialog.showDialog();
sessionUtils.ensureValidClient(app, context.JK.GearUtils, function() {
var sessionStartDialog = new JK.SessionStartDialog(JK.app, session);
sessionStartDialog.initialize();
return sessionStartDialog.showDialog();
});
}
this.addSessionLike = addSessionLike;

View File

@ -12,7 +12,8 @@ class ApiRsvpRequestsController < ApiController
music_session = MusicSession.find(params[:session_id])
# retrieve all requests for this session
if music_session.creator.id == current_user.id
creator = music_session.creator
if creator && creator.id == current_user.id
@rsvp_requests = RsvpRequest.index(music_session, nil, params)
# scope the response to the current user

View File

@ -0,0 +1,8 @@
task :stress_resque do
Rake::Task['environment'].invoke
10.times do
Resque.enqueue(StressJob)
end
end

View File

@ -203,4 +203,32 @@ describe "Feed", :js => true, :type => :feature, :capybara_feature => true do
end
describe "session participants behavior (VRFS-2193)" do
let(:creator) { FactoryGirl.create(:user) }
let(:finder_1) { FactoryGirl.create(:user) }
let(:finder_2) { FactoryGirl.create(:user) }
specify "after session ends all participants are shown in Feed" do
creator, description = create_join_session creator, finder_1
# feed shows user, finder_1
formal_leave_by(finder_1)
# feed shows user
join_session(finder_2, description: description)
# feed shows user, finder_2
formal_leave_by(finder_2)
formal_leave_by(creator)
#leave_music_session_sleep_delay #erg
in_client(creator) { verify_feed_shows_users creator, finder_1, finder_2 }
end
specify "during session only current participants are shown in Feed" do
creator, description = create_join_session creator, finder_1
formal_leave_by(finder_1)
join_session(finder_2, description: description)
#leave_music_session_sleep_delay #erg
in_client(finder_1) { verify_feed_shows_users finder_2, creator }
end
end
end

View File

@ -21,7 +21,7 @@ describe "In a Session", :js => true, :type => :feature, :capybara_feature => tr
description = "Public or private, I cant decide!"
create_session(creator: user, description: description)
in_client(user) do
set_session_as_private
set_session_access :private
end
in_client(finder) do
emulate_client
@ -31,7 +31,7 @@ describe "In a Session", :js => true, :type => :feature, :capybara_feature => tr
sign_out_poltergeist(validate: true)
end
in_client(user) do
set_session_as_public
set_session_access :public
end
join_session(finder, description: description) # verify the public session is able to be joined
end
@ -88,7 +88,7 @@ describe "In a Session", :js => true, :type => :feature, :capybara_feature => tr
creator, description = create_join_session(user, [finder])
in_client(user) do
set_session_as_private
set_session_access :private
formal_leave_by user
sign_out_poltergeist user
end

View File

@ -464,6 +464,12 @@ def join_session(joiner, options)
end
end
def request_to_join_session(joiner, options)
join_session(joiner, options.merge(no_verify: true))
find('#btn-alert-ok').trigger(:click)
# page.should have_no_selector('h1', text: 'Alert')
end
def emulate_client
@ -475,7 +481,7 @@ def create_join_session(creator, joiners=[], options={})
creator, unique_session_desc = create_session(options)
# find session in second client
joiners.each do |joiner|
[*joiners].each do |joiner|
join_session(joiner, description: unique_session_desc)
end
@ -490,6 +496,21 @@ def formal_leave_by user
end
end
def verify_feed_shows_users *users
users = [*users]
visit "/client#/feed"
find('.feed-details a.details').trigger(:click)
within 'div.music-session-history-entry' do
users.each do |user|
# confirm user avatar exists
find("a.avatar-tiny[user-id=\"#{user.id}\"][hoveraction=\"musician\"] img")
# confirm user name exists
find("a.musician-name[user-id=\"#{user.id}\"][hoveraction=\"musician\"]", text: user.name)
end
end
end
def start_recording_with(creator, joiners=[], genre=nil)
create_join_session(creator, joiners, {genre: genre})
in_client(creator) do
@ -529,22 +550,21 @@ def claim_recording(name, description)
page.should have_no_selector('h1', text: 'recording finished')
end
def set_session_as_private()
find('#session-settings-button').trigger(:click)
within('#session-settings-dialog') do
jk_select("Only RSVP musicians may join", '#session-settings-dialog #session-settings-musician-access')
#select('Private', :from => 'session-settings-musician-access')
find('#session-settings-dialog-submit').trigger(:click)
def set_session_access access_type
case access_type
when :only_rsvp, :private
access_level = "Only RSVP musicians may join"
when :by_approval
access_level = "Musicians may join by approval"
when :at_will, :public, :open
access_level = "Musicians may join at will"
else
access_level = "Musicians may join at will"
end
# verify it's dismissed
page.should have_no_selector('h1', text: 'update session settings')
end
def set_session_as_public()
find('#session-settings-button').trigger(:click)
within('#session-settings-dialog') do
jk_select("Musicians may join at will", '#session-settings-dialog #session-settings-musician-access')
# select('Public', :from => 'session-settings-musician-access')
jk_select(access_level, '#session-settings-dialog #session-settings-musician-access')
find('#session-settings-dialog-submit').trigger(:click)
end
# verify it's dismissed