Merge branch 'develop' of bitbucket.org:jamkazam/jam-cloud into develop

This commit is contained in:
Jonathan Kolyer 2014-07-27 23:39:36 +00:00
commit a1cb5baf52
21 changed files with 786 additions and 227 deletions

View File

@ -97,7 +97,7 @@ end
# gem 'capistrano'
# To use debugger
gem 'debugger'
#gem 'debugger' # not working with 2.1.2p95
group :development, :test do
gem 'capybara'

View File

@ -0,0 +1,100 @@
ActiveAdmin.register_page "Download CSV" do
menu :parent => 'Score'
page_action :create_csv, :method => :post do
puts params.inspect
start_time = params[:score_exports][:start]
end_time = params[:score_exports][:end]
if start_time.blank?
start_time = '1900-01-01'
end
start_time = "#{start_time}"
if end_time.blank?
end_time = Time.now + 1.days
else
end_time = "#{end_time}"
end
scores = ScoreHistory
.select("from_city, from_regions.regionname as from_region_name, from_countries.countryname as from_country_name, from_isp,
to_city, to_regions.regionname as to_region_name, to_countries.countryname as to_country_name, to_isp,
min(score_histories.score) as min_latency, max(score_histories.score) as max_latency, avg(score_histories.score) as mean_latency, median(CAST(score_histories.score AS NUMERIC)) as median_latency, count(score_histories.score) as score_count")
.joins('LEFT JOIN countries AS from_countries ON from_countries.countrycode = from_country')
.joins('LEFT JOIN countries AS to_countries ON to_countries.countrycode = to_country')
.joins('LEFT JOIN regions AS from_regions ON from_regions.region = from_region')
.joins('LEFT JOIN regions AS to_regions ON to_regions.region = to_region')
.where("score_dt BETWEEN DATE '#{start_time}' AND DATE '#{end_time}'")
.order('from_city, from_regions.regionname, from_countries.countryname, from_isp, to_city, to_regions.regionname, to_countries.countryname, to_isp')
.group('from_city, from_regions.regionname, from_countries.countryname, from_isp, to_city, to_regions.regionname, to_countries.countryname, to_isp')
.limit(1_000_000)
csv_string = CSV.generate do |csv|
csv << ["From Country", "From Region", "From City", "From ISP", "To Country", "To Region", "To City", "To ISP", "Min Latency", "Max Latency", "Median Latency", "Mean Latency", 'Score Count']
scores.each do |score|
puts score.inspect
csv << [score.from_country_name, score.from_region_name, score.from_city, score.from_isp,
score.to_country_name, score.to_region_name, score.to_city, score.to_isp,
score[:min_latency], score[:max_latency], score[:median_latency], score[:mean_latency], score[:score_count]]
end
end
send_data csv_string,
:type => 'text/csv; charset=iso-8859-1; header=present',
:disposition => "attachment; filename=score_export-#{start_time}-#{end_time}.csv"
end
content :title => "Export Score" do
columns do
column do
semantic_form_for :score_exports, :url => admin_download_csv_create_csv_path, :builder => ActiveAdmin::FormBuilder do |f|
f.inputs do
f.input :start, :as => :datepicker
f.input :end, :as => :datepicker
end
f.actions do
f.action :submit, :label => 'Download CSV'
end
end
end
column do
panel "Usage" do
span "Select a start day and end day to generate a CSV with a score summary. Both fields are optional."
end
panel "Limitation 1" do
div do
span do "The system limits the number of rows exported to 1,000,000" end
end
end
panel "Limitation 2" do
div do
span do "This report uses the score_histories table, which can lag up to 1 hour behind data. You can force a score_history sweep by going to" end
span do link_to "Resque", "#{Gon.global.prefix}/resque/schedule" end
span do " and then clicking 'Queue Now' for ScoreHistorySweeper. When the job count goes from 1 to 0, the score_histories table is now completely up-to-date, and you can make a 'fresh' CSV." end
end
end
end
end
#panel "Upaid Registrations" do
# table_for Registration.unpaid.limit(10).order('created_at desc') do
# column "Registration" do |registration|
# link_to registration.id, admin_registration_path(registration)
# end
# column :user
# column :tour
# column "Payment" do |registration|
# status_tag((registration.paid? ? "Received" : "Pending"), (registration.paid? ? :ok : :warning))
# end
# end
#end
end
end

View File

@ -4,34 +4,78 @@ ActiveAdmin.register JamRuby::ScoreHistory, :as => 'Score History' do
config.batch_actions = false
config.clear_action_items!
config.filters = true
config.per_page = 100
filter :score
filter :score_dt
#filter :from_user_id_eq, :as => :autocomplete, :url => "#{Gon.global.prefix}/admin/users/autocomplete_user_email",
# :label => "From User", :required => false,
# :wrapper_html => { :style => "list-style: none" }
#autocomplete :user, :email, :full => true, :display_value => :autocomplete_display_name
filter :from_user_id, as: :string
filter :from_latency_tester_id
filter :from_isp
filter :from_country
filter :from_region
filter :from_city
filter :from_postal
filter :from_latitude
filter :from_latitude
filter :from_longitude
filter :to_user_id, as: :string
filter :to_latency_tester_id
filter :to_isp
filter :to_country
filter :to_region
filter :to_city
filter :to_postal
filter :to_latitude
filter :to_latitude
filter :to_longitude
before_filter only: :index do
@per_page = 1_000_000 if request.format == 'text/csv'
end
index do
column :score
column :score_dt
column "Score", :score
column "When", :score_dt
column "From User", :from_user_id do |score|
link_to score.from_user, admin_user_path(score.from_user) if score.from_user_id
end
column "From Latency Tester", :from_latency_tester_id do |score|
link_to score.from_latency_tester_id, admin_latency_testers_path if score.from_latency_tester_id
end
column "From IP", :from_addr do |score|
IPAddr.new(score.from_addr, Socket::AF_INET).to_s if score.from_addr
end
column "From ISP", :from_isp
column "From Country", :from_country
column "From Region", :from_region
column "From City", :from_city
column "From Postal", :from_postal
column "From Lat", :from_latitude
column "From Long", :from_longitude
column "From Client", :from_client_id
column :from_client_id
column :from_user_id
column :from_latency_tester_id
column :from_addr
column :from_isp
column :from_country
column :from_region
column :from_city
column :from_postal
column :from_latitude
column :from_longitude
column :to_client_id
column :to_user_id
column :to_latency_tester_id
column :to_addr
column :to_isp
column :to_country
column :to_region
column :to_city
column :to_postal
column :to_latitude
column :to_longitude
column "To User", :to_user_id do |score|
link_to score.to_user, admin_user_path(score.to_user) if score.to_user_id
end
column "To Latency Tester", :to_latency_tester_id do |score|
link_to score.to_latency_tester_id, admin_latency_testers_path if score.to_latency_tester_id
end
column "To IP", :to_addr do |score|
IPAddr.new(score.to_addr, Socket::AF_INET).to_s if score.to_addr
end
column "To ISP", :to_isp
column "To Country", :to_country
column "To Region", :to_region
column "To City", :to_city
column "To Postal", :to_postal
column "To Lat", :to_latitude
column "To Long", :to_longitude
column "To Client", :to_client_id
end
end

View File

@ -17,7 +17,7 @@
return $.ajax({
type: "POST",
dataType: "json",
url: gon.global.prefix + 'api/mix/' + mixId + '/enqueue',
url: gon.global.prefix + '/api/mix/' + mixId + '/enqueue',
contentType: 'application/json',
processData: false
});

View File

@ -9,7 +9,7 @@
var $link = $(this);
restAdmin.tryMixAgain({mix_id: $link.attr('data-mix-id')})
.done(function(response) {
$link.closest('div.mix-again').find('div.mix-again-dialog').html('<div>Mix enqueued</div><a href="' + gon.global.prefix + 'resque">Resque Web</a>').dialog();
$link.closest('div.mix-again').find('div.mix-again-dialog').html('<div>Mix enqueued</div><a href="' + gon.global.prefix + '/resque">Resque Web</a>').dialog();
})
.error(function(jqXHR) {
$link.closest('div.mix-again').find('div.mix-again-dialog').html('Mix failed: ' + jqXHR.responseText).dialog();

View File

@ -1 +1 @@
Gon.global.prefix = ENV['RAILS_RELATIVE_URL_ROOT'] || '/'
Gon.global.prefix = ENV['RAILS_RELATIVE_URL_ROOT'] || ''

2
admin/migrate.sh Executable file
View File

@ -0,0 +1,2 @@
#!/bin/bash
bundle exec jam_db up --connopts=dbname:jam host:localhost user:postgres password:postgres --verbose

View File

@ -195,4 +195,6 @@ max_mind_releases.sql
score_histories.sql
update_sms_index.sql
connection_allow_null_locidispid.sql
track_user_in_scores.sql
track_user_in_scores.sql
median_aggregate.sql
current_scores_use_median.sql

View File

@ -0,0 +1,13 @@
-- this results in a rough median; the only problem is that we don't avg if it's an even number. not a big deal truthfully, since eventually you'll have > 5
DROP VIEW current_scores;
CREATE OR REPLACE VIEW current_scores AS
SELECT * FROM (SELECT * , row_number() OVER (PARTITION BY alocidispid, blocidispid, scorer ORDER BY score DESC) AS pcnum FROM
(SELECT * FROM
(SELECT percent_rank() over (PARTITION BY alocidispid, blocidispid ORDER BY score ASC) AS pc, * FROM
(SELECT * FROM
(SELECT *, row_number() OVER (PARTITION BY alocidispid, blocidispid ORDER BY created_at DESC) AS rownum FROM scores) tmp
WHERE rownum < 6) AS score_ranked)
AS tmp2 WHERE pc <= .5 ORDER BY pc DESC) pcs )
AS final WHERE pcnum < 2;

View File

@ -0,0 +1,23 @@
-- from here: https://wiki.postgresql.org/wiki/Aggregate_Median
CREATE OR REPLACE FUNCTION _final_median(numeric[])
RETURNS numeric
AS
$body$
SELECT AVG(val)
FROM (
SELECT val
FROM unnest($1) val
ORDER BY 1
LIMIT 2 - MOD(array_upper($1, 1), 2)
OFFSET CEIL(array_upper($1, 1) / 2.0) - 1
) sub;
$body$
LANGUAGE sql ;
-- IMMUTABLE not accepted by pg migrate
CREATE AGGREGATE median(numeric) (
SFUNC=array_append,
STYPE=numeric[],
FINALFUNC=_final_median,
INITCOND='{}'
);

View File

@ -4,6 +4,9 @@ module JamRuby
self.table_name = 'score_histories'
belongs_to :from_user, class_name: 'JamRuby::User', foreign_key: 'from_user_id'
belongs_to :to_user, class_name: 'JamRuby::User', foreign_key: 'to_user_id'
def self.migrate_scores
generic_state = GenericState.singleton

View File

@ -390,7 +390,7 @@ module JamRuby
limit = 50
rel = User.musicians_geocoded
.where(['created_at >= ? AND users.id != ?', since_date, usr.id])
.where(['users.created_at >= ? AND users.id != ?', since_date, usr.id])
.joins('inner join current_scores on users.last_jam_locidispid = current_scores.alocidispid')
.where(['current_scores.blocidispid = ?', locidispid])
.where(['current_scores.score <= ?', score_limit])

View File

@ -116,6 +116,9 @@ module JamRuby
# diagnostics
has_many :diagnostics, :class_name => "JamRuby::Diagnostic"
# score history
has_many :from_score_histories, :class_name => "JamRuby::ScoreHistory", foreign_key: 'from_user_id'
has_many :to_score_histories, :class_name => "JamRuby::ScoreHistory", foreign_key: 'to_user_id'
# This causes the authenticate method to be generated (among other stuff)
#has_secure_password

View File

@ -10,141 +10,305 @@ describe Score do
let(:latency_tester2) { FactoryGirl.create(:latency_tester) }
let(:score_with_latency_tester) { s1, s2 = Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo', { alatencytestid: latency_tester1.id, blatencytestid: latency_tester2.id}); s1 }
before do
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 3456, 'cnodeid', 0x03040506, 30, nil)
Score.createx(1234, 'anodeid', 0x01020304, 3456, 'cnodeid', 0x03040506, 40, Time.new.utc-3600)
end
it "count" do
Score.count.should == 6
end
it 'a to b' do
s = Score.where(alocidispid: 1234, blocidispid: 2345).limit(1).first
s.should_not be_nil
s.alocidispid.should == 1234
s.anodeid.should eql('anodeid')
s.aaddr.should == 0x01020304
s.blocidispid.should == 2345
s.bnodeid.should eql('bnodeid')
s.baddr.should == 0x02030405
s.score.should == 20
s.scorer.should == 0
s.score_dt.should_not be_nil
s.scoring_data.should eq('foo')
end
it 'b to a' do
s = Score.where(alocidispid: 2345, blocidispid: 1234).limit(1).first
s.should_not be_nil
s.alocidispid.should == 2345
s.anodeid.should eql('bnodeid')
s.aaddr.should == 0x02030405
s.blocidispid.should == 1234
s.bnodeid.should eql('anodeid')
s.baddr.should == 0x01020304
s.score.should == 20
s.scorer.should == 1
s.score_dt.should_not be_nil
s.scoring_data.should be_nil
end
it 'a to c' do
s = Score.where(alocidispid: 1234, blocidispid: 3456).limit(1).first
s.should_not be_nil
s.alocidispid.should == 1234
s.anodeid.should eql('anodeid')
s.aaddr.should == 0x01020304
s.blocidispid.should == 3456
s.bnodeid.should eql('cnodeid')
s.baddr.should == 0x03040506
s.score.should == 30
s.scorer.should == 0
s.score_dt.should_not be_nil
s.scoring_data.should be_nil
end
it 'c to a' do
s = Score.where(alocidispid: 3456, blocidispid: 1234).limit(1).first
s.should_not be_nil
s.alocidispid.should == 3456
s.anodeid.should eql('cnodeid')
s.aaddr.should == 0x03040506
s.blocidispid.should == 1234
s.bnodeid.should eql('anodeid')
s.baddr.should == 0x01020304
s.score.should == 30
s.scorer.should == 1
s.score_dt.should_not be_nil
s.scoring_data.should be_nil
end
it 'delete a to c' do
Score.deletex(1234, 3456)
Score.count.should == 2
Score.where(alocidispid: 1234, blocidispid: 3456).limit(1).first.should be_nil
Score.where(alocidispid: 3456, blocidispid: 1234).limit(1).first.should be_nil
Score.where(alocidispid: 1234, blocidispid: 2345).limit(1).first.should_not be_nil
Score.where(alocidispid: 2345, blocidispid: 1234).limit(1).first.should_not be_nil
end
it 'findx' do
Score.findx(1234, 1234).should == -1
Score.findx(1234, 2345).should == 20
Score.findx(1234, 3456).should == 30
Score.findx(2345, 1234).should == 20
Score.findx(2345, 2345).should == -1
Score.findx(2345, 3456).should == -1
Score.findx(3456, 1234).should == 30
Score.findx(3456, 2345).should == -1
Score.findx(3456, 3456).should == -1
end
it "test shortcut for making scores from connections" do
user1 = FactoryGirl.create(:user)
conn1 = FactoryGirl.create(:connection, user: user1, addr: 0x01020304, locidispid: 5)
user2 = FactoryGirl.create(:user)
conn2 = FactoryGirl.create(:connection, user: user2, addr: 0x11121314, locidispid: 6)
user3 = FactoryGirl.create(:user)
conn3 = FactoryGirl.create(:connection, user: user3, addr: 0x21222324, locidispid: 7)
Score.findx(5, 6).should == -1
Score.findx(6, 5).should == -1
Score.findx(5, 7).should == -1
Score.findx(7, 5).should == -1
Score.findx(6, 7).should == -1
Score.findx(7, 6).should == -1
Score.score_conns(conn1, conn2, 12)
Score.score_conns(conn1, conn3, 13)
Score.score_conns(conn2, conn3, 23)
Score.findx(5, 6).should == 12
Score.findx(6, 5).should == 12
Score.findx(5, 7).should == 13
Score.findx(7, 5).should == 13
Score.findx(6, 7).should == 23
Score.findx(7, 6).should == 23
end
describe "createx" do
it "creates with user info" do
score_with_user.touch
score_with_user.auserid.should == user1.id
score_with_user.buserid.should == user2.id
score_with_user.alatencytestid.should be_nil
score_with_user.blatencytestid.should be_nil
describe "with default scores" do
before do
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 3456, 'cnodeid', 0x03040506, 30, nil)
Score.createx(1234, 'anodeid', 0x01020304, 3456, 'cnodeid', 0x03040506, 40, Time.new.utc-3600)
end
it "creates with latency-tester info" do
score_with_latency_tester.touch
score_with_latency_tester.auserid.should be_nil
score_with_latency_tester.buserid.should be_nil
score_with_latency_tester.alatencytestid.should == latency_tester1.id
score_with_latency_tester.blatencytestid.should == latency_tester2.id
it "count" do
Score.count.should == 6
end
it 'a to b' do
s = Score.where(alocidispid: 1234, blocidispid: 2345).limit(1).first
s.should_not be_nil
s.alocidispid.should == 1234
s.anodeid.should eql('anodeid')
s.aaddr.should == 0x01020304
s.blocidispid.should == 2345
s.bnodeid.should eql('bnodeid')
s.baddr.should == 0x02030405
s.score.should == 20
s.scorer.should == 0
s.score_dt.should_not be_nil
s.scoring_data.should eq('foo')
end
it 'b to a' do
s = Score.where(alocidispid: 2345, blocidispid: 1234).limit(1).first
s.should_not be_nil
s.alocidispid.should == 2345
s.anodeid.should eql('bnodeid')
s.aaddr.should == 0x02030405
s.blocidispid.should == 1234
s.bnodeid.should eql('anodeid')
s.baddr.should == 0x01020304
s.score.should == 20
s.scorer.should == 1
s.score_dt.should_not be_nil
s.scoring_data.should be_nil
end
it 'a to c' do
s = Score.where(alocidispid: 1234, blocidispid: 3456).limit(1).first
s.should_not be_nil
s.alocidispid.should == 1234
s.anodeid.should eql('anodeid')
s.aaddr.should == 0x01020304
s.blocidispid.should == 3456
s.bnodeid.should eql('cnodeid')
s.baddr.should == 0x03040506
s.score.should == 30
s.scorer.should == 0
s.score_dt.should_not be_nil
s.scoring_data.should be_nil
end
it 'c to a' do
s = Score.where(alocidispid: 3456, blocidispid: 1234).limit(1).first
s.should_not be_nil
s.alocidispid.should == 3456
s.anodeid.should eql('cnodeid')
s.aaddr.should == 0x03040506
s.blocidispid.should == 1234
s.bnodeid.should eql('anodeid')
s.baddr.should == 0x01020304
s.score.should == 30
s.scorer.should == 1
s.score_dt.should_not be_nil
s.scoring_data.should be_nil
end
it 'delete a to c' do
Score.deletex(1234, 3456)
Score.count.should == 2
Score.where(alocidispid: 1234, blocidispid: 3456).limit(1).first.should be_nil
Score.where(alocidispid: 3456, blocidispid: 1234).limit(1).first.should be_nil
Score.where(alocidispid: 1234, blocidispid: 2345).limit(1).first.should_not be_nil
Score.where(alocidispid: 2345, blocidispid: 1234).limit(1).first.should_not be_nil
end
it 'findx' do
Score.findx(1234, 1234).should == -1
Score.findx(1234, 2345).should == 20
Score.findx(1234, 3456).should == 30
Score.findx(2345, 1234).should == 20
Score.findx(2345, 2345).should == -1
Score.findx(2345, 3456).should == -1
Score.findx(3456, 1234).should == 30
Score.findx(3456, 2345).should == -1
Score.findx(3456, 3456).should == -1
end
it "test shortcut for making scores from connections" do
user1 = FactoryGirl.create(:user)
conn1 = FactoryGirl.create(:connection, user: user1, addr: 0x01020304, locidispid: 5)
user2 = FactoryGirl.create(:user)
conn2 = FactoryGirl.create(:connection, user: user2, addr: 0x11121314, locidispid: 6)
user3 = FactoryGirl.create(:user)
conn3 = FactoryGirl.create(:connection, user: user3, addr: 0x21222324, locidispid: 7)
Score.findx(5, 6).should == -1
Score.findx(6, 5).should == -1
Score.findx(5, 7).should == -1
Score.findx(7, 5).should == -1
Score.findx(6, 7).should == -1
Score.findx(7, 6).should == -1
Score.score_conns(conn1, conn2, 12)
Score.score_conns(conn1, conn3, 13)
Score.score_conns(conn2, conn3, 23)
Score.findx(5, 6).should == 12
Score.findx(6, 5).should == 12
Score.findx(5, 7).should == 13
Score.findx(7, 5).should == 13
Score.findx(6, 7).should == 23
Score.findx(7, 6).should == 23
end
describe "createx" do
it "creates with user info" do
score_with_user.touch
score_with_user.auserid.should == user1.id
score_with_user.buserid.should == user2.id
score_with_user.alatencytestid.should be_nil
score_with_user.blatencytestid.should be_nil
end
it "creates with latency-tester info" do
score_with_latency_tester.touch
score_with_latency_tester.auserid.should be_nil
score_with_latency_tester.buserid.should be_nil
score_with_latency_tester.alatencytestid.should == latency_tester1.id
score_with_latency_tester.blatencytestid.should == latency_tester2.id
end
end
end
# current_scores is a view that tries to take the median of up to the last 5 entries
describe "current_scores" do
it "works with empty data set" do
result = Score.connection.execute('SELECT * FROM current_scores')
result.check
result.ntuples.should == 0
end
it "works with one score" do
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo')
result = Score.connection.execute('SELECT * FROM current_scores')
result.check
result.ntuples.should == 2
result[0]['alocidispid'].to_i.should == 1234
result[0]['scorer'].to_i.should == 0
result[1]['alocidispid'].to_i.should == 2345
result[1]['scorer'].to_i.should == 1
end
it "works with two scores in same location" do
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo') # median
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 25, nil, 'foo')
result = Score.connection.execute('SELECT * FROM current_scores')
result.check
result.ntuples.should == 2
result[0]['score'].to_i.should == 20
result[1]['score'].to_i.should == 20
end
it "works with three scores in same location" do
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 25, nil, 'foo') # median
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 30, nil, 'foo')
result = Score.connection.execute('SELECT * FROM current_scores')
result.check
result.ntuples.should == 2
result[0]['score'].to_i.should == 25
result[1]['score'].to_i.should == 25
end
it "works with six scores in same location" do
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo') # we'll make sure this is old, so it won't be in the set
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 25, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 30, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 31, nil, 'foo')# median
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 32, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 33, nil, 'foo')
Score.connection.execute("UPDATE scores set created_at = TIMESTAMP '#{1.days.ago}' WHERE score = 20").cmdtuples.should == 2
result = Score.connection.execute('SELECT * FROM current_scores')
result.check
result.ntuples.should == 2
result[0]['score'].to_i.should == 31
result[1]['score'].to_i.should == 31
# now push back score with 33 to the very back, which will shift the median up to 30
Score.connection.execute("UPDATE scores set created_at = TIMESTAMP '#{2.days.ago}' WHERE score = 33").check
result = Score.connection.execute('SELECT * FROM current_scores')
result.check
result.ntuples.should == 2
result[0]['score'].to_i.should == 30
result[1]['score'].to_i.should == 30
end
it "works with one score each in different locations" do
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo') # median
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 25, nil, 'foo')
result = Score.connection.execute('SELECT * FROM current_scores ORDER BY score')
result.check
result.ntuples.should == 4
result[0]['score'].to_i.should == 20
result[1]['score'].to_i.should == 20
result[2]['score'].to_i.should == 25
result[3]['score'].to_i.should == 25
end
it "works with multiple scores in different locations" do
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo') # median
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 25, nil, 'foo') # median
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 30, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 35, nil, 'foo')
result = Score.connection.execute('SELECT * FROM current_scores ORDER BY score')
result.check
result.ntuples.should == 4
result[0]['score'].to_i.should == 20
result[1]['score'].to_i.should == 20
result[2]['score'].to_i.should == 25
result[3]['score'].to_i.should == 25
end
it "works with multiple scores in different locations" do
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 25, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 30, nil, 'foo') # median
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 35, nil, 'foo') # median
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 40, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 45, nil, 'foo')
result = Score.connection.execute('SELECT * FROM current_scores ORDER BY score')
result.check
result.ntuples.should == 4
result[0]['score'].to_i.should == 30
result[1]['score'].to_i.should == 30
result[2]['score'].to_i.should == 35
result[3]['score'].to_i.should == 35
end
it "works with over 6 scores in different locations" do
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 25, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 30, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 35, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 40, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 45, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 45, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 50, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 55, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 60, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 65, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 2346, 'bnodeid', 0x02030405, 70, nil, 'foo')
Score.connection.execute("UPDATE scores set created_at = TIMESTAMP '#{1.days.ago}' WHERE score = 20 OR score = 25").cmdtuples.should == 4
result = Score.connection.execute('SELECT * FROM current_scores ORDER BY score')
result.check
result.ntuples.should == 4
result[0]['score'].to_i.should == 45
result[1]['score'].to_i.should == 45
result[2]['score'].to_i.should == 50
result[3]['score'].to_i.should == 50
Score.connection.execute("UPDATE scores set created_at = TIMESTAMP '#{2.days.ago}' WHERE score = 65 OR score = 70").cmdtuples.should == 4
result = Score.connection.execute('SELECT * FROM current_scores ORDER BY score')
result.check
result.ntuples.should == 4
result[0]['score'].to_i.should == 40
result[1]['score'].to_i.should == 40
result[2]['score'].to_i.should == 45
result[3]['score'].to_i.should == 45
end
end

View File

@ -2,7 +2,10 @@
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
#require 'resque/tasks'
#require 'resque/scheduler/tasks'
require 'resque/tasks'
require 'resque/scheduler/tasks'
require File.expand_path('../config/application', __FILE__)
SampleApp::Application.load_tasks

View File

@ -16,7 +16,7 @@
<!-- @end sort filter -->
<% else %>
<!-- @begin order by filter -->
<%= content_tag(:div, 'Filter By:', :class => 'filter-element desc') %>
<%= content_tag(:div, 'Order By:', :class => 'filter-element desc') %>
<%= select_tag("#{filter_label}_order_by", options_for_select(Search::ORDERINGS), {:class => "#{filter_label}-order-by easydropdown"} ) %>
<!-- @end order by filter -->
<% end %>

View File

@ -1,7 +1,6 @@
#!/bin/bash
set -e
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# 'target' is the output directory

View File

@ -1,8 +1,8 @@
# Resque tasks
require 'resque/tasks'
require 'resque_scheduler/tasks'
require 'resque/scheduler/tasks'
require 'resque'
require 'resque_scheduler'
require 'resque-scheduler'
task :scheduler => :environment do

View File

@ -1,6 +1,6 @@
require 'spec_helper'
describe "Create Session Flow", :js => true, :type => :feature, :capybara_feature => true do
describe "Create Session UI", :js => true, :type => :feature, :capybara_feature => true do
let(:user1) { FactoryGirl.create(:user) }
let(:user2) { FactoryGirl.create(:user) }
@ -13,8 +13,7 @@ describe "Create Session Flow", :js => true, :type => :feature, :capybara_featur
page.driver.resize(1500, 800) # makes sure all the elements are visible
emulate_client
sign_in_poltergeist user1
wait_until_curtain_gone
visit "/client#/createSession"
page.find('.createsession').trigger(:click)
end
end
@ -211,48 +210,4 @@ describe "Create Session Flow", :js => true, :type => :feature, :capybara_featur
end
end
end
context "create session flow backend" do
describe "schedule a session" do
it "schedule a session" do
schedule_session({creator: user1})
end
it "start a session after scheduling" do
MusicSession.delete_all
schedule_session({creator: user1})
in_client(user1) do
visit "/client#/createSession"
find('li[create-type="start-scheduled"] ins').trigger(:click)
find('.btn-next').trigger(:click)
find('.btn-next').trigger(:click)
expect(page).to have_selector('h2', text: 'my tracks')
find('#session-screen .session-mytracks .session-track')
end
end
end
it "start quick session" do
page.driver.resize(1500, 800) # makes sure all the elements are visible
emulate_client
sign_in_poltergeist user1
wait_until_curtain_gone
visit "/client#/createSession"
expect(page).to have_selector('h1', text: 'create session')
find('li[create-type="quick-start"] ins').trigger(:click)
find('div[info-id="quick-start"]')
find('.btn-next').trigger(:click)
find('.btn-next', text: 'START SESSION').trigger(:click)
expect(page).to have_selector('h2', text: 'my tracks')
find('#session-screen .session-mytracks .session-track')
end
end
end

View File

@ -0,0 +1,231 @@
require 'spec_helper'
describe "Create Session", :js => true, :type => :feature, :capybara_feature => true do
let(:user1) { FactoryGirl.create(:user) }
let(:user2) { FactoryGirl.create(:user) }
context "functionally test all ways to Create Session" do
context "I have already scheduled a session..." do
let (:now) { Time.now - 5.hours }
let (:first_session) { FactoryGirl.create(:music_session, creator: user1, name: "First one", scheduled_start: now + 5.minutes) }
let (:second_session) { FactoryGirl.create(:music_session, creator: user1, name: "Second one", scheduled_start: now + 2.hours) }
let (:third_session) { FactoryGirl.create(:music_session, creator: user1, name: "Third one", scheduled_start: now + 17.days) }
let (:not_my_session) { FactoryGirl.create(:music_session, creator: user2, name: "Who cares", scheduled_start: now + 30.minutes) }
before do
#instantiate these test sessions in non-sequential order
third_session.touch; first_session.touch; not_my_session.touch; second_session.touch;
#[first_session, second_session, third_session, not_my_session].each { |s| puts "#{s.name}: #{s.id}" }
emulate_client
page.driver.resize(1500, 800) #purely aesthetic
sign_in_poltergeist user1
find('.createsession').trigger(:click)
wait_for_ajax
expect(page).to have_selector 'li[create-type="start-scheduled"] ins'
end
it "sessions are shown in schedule order on the Create Session screen" do
sleep 2 #arg
radio_buttons = page.all('ul#scheduled-session-list li')
first, second, third = *radio_buttons[0..2]
expect(first.text).to include first_session.name
expect(second.text).to include second_session.name
expect(third.text).to include third_session.name
expect(first).to have_selector 'input[checked=checked]'
expect(page).to_not have_text not_my_session.name
end
it "future sessions can be edited from the Create Session screen" do
#pending "possible bug, does not occur when testing manually"
page.find('a#edit_scheduled_sessions').trigger(:click)
#expect(page).to have_selector "div[data-id='#{first_session.id}']" #see pending note
expect(page).to have_selector "div[data-id='#{second_session.id}']"
expect(page).to have_selector "div[data-id='#{third_session.id}']"
expect(page).to_not have_selector "div[data-id='#{not_my_session.id}']"
end
context "...and I want to start it now" do
it "starts the first one" do
sleep 1
find('.btn-next').trigger(:click)
sleep 1
expect(page).to have_selector('.session-step-title', text: 'Review & Confirm')
expect(page).to have_content first_session.name
find('.btn-next').trigger(:click)
expect(page).to have_selector('h2', text: 'my tracks')
find('#session-screen .session-mytracks .session-track')
end
context "attempt to start a session more than an hour from now" do
let (:first_session) { FactoryGirl.create(:music_session, creator: user1, scheduled_start: now + 65.minutes) }
it "warns the user that session starts in the future, and user can start session" do
sleep 1
find('.btn-next').trigger(:click)
sleep 1
expect(page).to have_selector('h1', text: 'Future Session')
expect(page).to have_content "Are you sure"
find('#btn-confirm-ok', text: 'Start Session Now').trigger(:click)
sleep 1
expect(page).to have_selector('.session-step-title', text: 'Review & Confirm')
expect(page).to have_content first_session.name
find('.btn-next').trigger(:click)
expect(page).to have_selector('h2', text: 'my tracks')
find('#session-screen .session-mytracks .session-track')
end
end
end
end
shared_examples_for :a_future_session do
specify "creator can see the session on Create Session page" do
in_client(creator) do
page.find('.createsession').trigger(:click)
expect(page).to have_selector('h1', text: 'create session')
sessions = page.first('ul#scheduled-session-list li')
expect(sessions.text).to include session_name
end
end
specify "creator can see the session on Find Session page" do
in_client(creator) do
visit "/client#/findSession"
wait_until_curtain_gone
expect(page).to have_selector('#session-name-disp', text: "#{session_name} (#{session_genre})")
#expect(page).to have_selector('#session-name-disp', text: @session_genre)
end
end
specify "another user can see the session on Find Session page" do
in_client(someone_else) do
emulate_client
page.driver.resize(1500, 800)
sign_in_poltergeist someone_else
visit "/client#/findSession"
wait_until_curtain_gone
expect(find('table#sessions-scheduled')).to have_content session_name
expect(find('table#sessions-scheduled')).to have_content session_genre
end
end
specify "another user can RSVP to the session" do
in_client(someone_else) do
emulate_client
page.driver.resize(1500, 800)
sign_in_poltergeist someone_else
visit "/client#/findSession"
wait_until_curtain_gone
within('table#sessions-scheduled') do
find('a.rsvp-link').trigger(:click)
end
within('div.dialog-inner') do
find('div.session-name').should have_content session_name
find('div.slot-instructions').should have_content "Check the box(es) next to the track(s) you want to play"
# fill_in '.txtComment', with: "Looking forward to the session"
#first('div.rsvp-instruments input').trigger(:click)
sleep 1
find('#btnSubmitRsvp').trigger(:click)
sleep 2
end
end
end
specify "creator can start the session" do
in_client(creator) do
page.find('.createsession').trigger(:click)
expect(page).to have_selector('h1', text: 'create session')
expect(page).to have_content session_name
find('li[create-type="start-scheduled"] ins').trigger(:click)
find('.btn-next').trigger(:click)
find('.btn-next').trigger(:click)
expect(page).to have_selector('h2', text: 'my tracks')
find('#session-screen .session-mytracks .session-track')
end
end
end
context "I want to schedule a session for a specific future time" do
before do
MusicSession.delete_all
@creator, @session_name, @session_genre = schedule_session(creator: user1)
end
it_should_behave_like :a_future_session do
let(:creator) { @creator }
let(:session_name) { @session_name }
let(:session_genre) { @session_genre }
let(:someone_else) { FactoryGirl.create(:user) }
end
end
context "I want to choose the time after others RSVP to my session" do
before do
MusicSession.delete_all
@creator, @session_name, @session_genre = schedule_session(creator: user1, rsvp: true)
end
it_should_behave_like :a_future_session do
let(:creator) { @creator }
let(:session_name) { @session_name }
let(:session_genre) { @session_genre }
let(:someone_else) { FactoryGirl.create(:user) }
end
end
context "I want to start a new session right now for others to join" do
before do
MusicSession.delete_all
@creator, @session_name, @session_genre = schedule_session(creator: user1, immediate: true)
end
specify "creator is in the session" do
in_client @creator do
expect(page).to have_selector('h2', text: 'my tracks')
find('#session-screen .session-mytracks .session-track')
end
end
specify "another user can see the session on Find Session page" do
in_client(user2) do
emulate_client
page.driver.resize(1500, 800)
sign_in_poltergeist user2
visit "/client#/findSession"
wait_until_curtain_gone
expect(find('table#sessions-scheduled')).to have_content @session_name
expect(find('table#sessions-scheduled')).to have_content @session_genre
end
end
end
context "I want to quick start a test session just for me" do
before do
MusicSession.delete_all
@creator, @session_name, @session_genre = schedule_session(creator: user1, quickstart: true)
end
specify "creator is in the session" do
in_client @creator do
expect(page).to have_selector('h2', text: 'my tracks')
find('#session-screen .session-mytracks .session-track')
end
end
specify "another user does NOT see the session on Find Session page" do
in_client(user2) do
emulate_client
page.driver.resize(1500, 800)
sign_in_poltergeist user2
visit "/client#/findSession"
wait_until_curtain_gone
expect(find('table#sessions-scheduled')).to_not have_content @session_name
end
end
end
end
end

View File

@ -335,6 +335,9 @@ def schedule_session(options = {})
fan_chat = options[:fan_chat].nil? ? false : options[:fan_chat]
musician_access_value = 'Musicians may join by approval'
fan_permission_value = 'Fans may listen, chat with each other'
rsvp = options[:rsvp]
immediate = options[:immediate]
quickstart = options[:quickstart]
if musician_access && !approval_required
musician_access_value = 'Musicians may join at will'
@ -356,26 +359,40 @@ def schedule_session(options = {})
expect(page).to have_selector('h1', text: 'create session')
within('#create-session-form') do
find('li[create-type="schedule-future"] ins').trigger(:click)
find('.btn-next').trigger(:click)
jk_select(genre, '#create-session-form select[name="genres"]')
fill_in('session-name', :with => unique_session_name)
fill_in('session-description', :with => unique_session_desc)
find('.btn-next').trigger(:click)
if rsvp
find('li[create-type="rsvp"] ins').trigger(:click)
elsif immediate
find('li[create-type="immediately"] ins').trigger(:click)
elsif quickstart
find('li[create-type="quick-start"] ins').trigger(:click)
else
find('li[create-type="schedule-future"] ins').trigger(:click)
end
find('.btn-next').trigger(:click)
find('div#divSessionPolicy ins').trigger(:click)
jk_select(musician_access_value, '#session-musician-access')
jk_select(fan_permission_value, '#session-fans-access')
find('.btn-next').trigger(:click)
unless quickstart
jk_select(genre, '#create-session-form select[name="genres"]')
fill_in('session-name', :with => unique_session_name)
fill_in('session-description', :with => unique_session_desc)
find('.btn-next').trigger(:click)
find('.btn-next', text: 'PUBLISH SESSION').trigger(:click)
find('.btn-next').trigger(:click)
find('div#divSessionPolicy ins').trigger(:click)
jk_select(musician_access_value, '#session-musician-access')
jk_select(fan_permission_value, '#session-fans-access')
find('.btn-next').trigger(:click)
end
unless quickstart || immediate
find('.btn-next', text: 'PUBLISH SESSION').trigger(:click)
else
find('.btn-next', text: 'START SESSION').trigger(:click)
end
end
find('h2', text: 'create session')
# find('h2', text: 'create session') unless quickstart || immediate
sleep 1 # to get rid of this, we need to verify that the URL is /client#/home.. otherwise intermittent fails
end