From 2dd6153ed86191a2a752fb11f5e2ef08eead3054 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sat, 26 Jul 2014 14:18:17 -0500 Subject: [PATCH 01/10] * wip on score export --- admin/app/admin/score_export.rb | 68 ++++++++++++++++++ admin/app/admin/score_history.rb | 88 ++++++++++++++++------- ruby/lib/jam_ruby/models/score_history.rb | 3 + ruby/lib/jam_ruby/models/user.rb | 3 + 4 files changed, 137 insertions(+), 25 deletions(-) create mode 100644 admin/app/admin/score_export.rb diff --git a/admin/app/admin/score_export.rb b/admin/app/admin/score_export.rb new file mode 100644 index 000000000..d593f2b58 --- /dev/null +++ b/admin/app/admin/score_export.rb @@ -0,0 +1,68 @@ +ActiveAdmin.register_page "Score Exports" 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 = "DATE '#{start_time}'" + if end_time.blank? + end_time = 'NOW()' + else + end_time = "DATE '#{end_time}'" + end + + @users = User.find(:all) + Fetching all user details and assigned on @users + csv_string = CSV.generate do |csv| + Using CSV class generate method to create csv file + csv << ["Id", "Name", "Email","Role"] + Creating header of CSV file + + @users.each do |user| + csv << [user.id, user.name, user.name, user.role] + end + Retrieving each rows and assigning on csv_string variable. + + + send_data csv_string, + :type => 'text/csv; charset=iso-8859-1; header=present', + :disposition => "attachment; +filename=users.csv" + + render 'hi' + # redirect_to admin_bootstrap_path, :notice => "Server created. If you start a job worker (bundle exec rake all_jobs in /web), it should update your icecast config." + end + + + content :title => "Score Exports" do + + semantic_form_for :score_exports, :url => admin_score_exports_create_csv_path, :builder => ActiveAdmin::FormBuilder do |f| + f.inputs do + f.input :start, :as => :datepicker + f.input :end, :as => :datepicker + end + f.buttons + 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 \ No newline at end of file diff --git a/admin/app/admin/score_history.rb b/admin/app/admin/score_history.rb index ccd827eac..3fb0c5d17 100644 --- a/admin/app/admin/score_history.rb +++ b/admin/app/admin/score_history.rb @@ -4,34 +4,72 @@ ActiveAdmin.register JamRuby::ScoreHistory, :as => 'Score History' do config.batch_actions = false config.clear_action_items! config.filters = true + config.per_page = 10 + + filter :score filter :score_dt + 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 diff --git a/ruby/lib/jam_ruby/models/score_history.rb b/ruby/lib/jam_ruby/models/score_history.rb index 5c17640f6..ed90892d4 100644 --- a/ruby/lib/jam_ruby/models/score_history.rb +++ b/ruby/lib/jam_ruby/models/score_history.rb @@ -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 diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index 5f241d71b..06d7d97e4 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -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 From 44ba9ef441be524ac6740d59362a7299cbcaf6bc Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 27 Jul 2014 11:45:46 -0500 Subject: [PATCH 02/10] * VRFS-1496 - export CSV --- admin/app/admin/score_export.rb | 82 +++++++++++++++++++++++---------- db/manifest | 3 +- db/up/median_aggregate.sql | 23 +++++++++ 3 files changed, 83 insertions(+), 25 deletions(-) create mode 100644 db/up/median_aggregate.sql diff --git a/admin/app/admin/score_export.rb b/admin/app/admin/score_export.rb index d593f2b58..5e4802a0d 100644 --- a/admin/app/admin/score_export.rb +++ b/admin/app/admin/score_export.rb @@ -11,46 +11,80 @@ ActiveAdmin.register_page "Score Exports" do if start_time.blank? start_time = '1900-01-01' end - start_time = "DATE '#{start_time}'" + start_time = "#{start_time}" if end_time.blank? - end_time = 'NOW()' + end_time = Time.now + 1.days else - end_time = "DATE '#{end_time}'" + end_time = "#{end_time}" end - @users = User.find(:all) - Fetching all user details and assigned on @users + puts "start_time #{start_time}, end_time #{end_time}" + + 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| - Using CSV class generate method to create csv file - csv << ["Id", "Name", "Email","Role"] - Creating header of CSV file + 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'] - @users.each do |user| - csv << [user.id, user.name, user.name, user.role] + 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 - Retrieving each rows and assigning on csv_string variable. + end - send_data csv_string, - :type => 'text/csv; charset=iso-8859-1; header=present', - :disposition => "attachment; -filename=users.csv" - - render 'hi' - # redirect_to admin_bootstrap_path, :notice => "Server created. If you start a job worker (bundle exec rake all_jobs in /web), it should update your icecast config." + 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 => "Score Exports" do - - semantic_form_for :score_exports, :url => admin_score_exports_create_csv_path, :builder => ActiveAdmin::FormBuilder do |f| - f.inputs do - f.input :start, :as => :datepicker - f.input :end, :as => :datepicker + columns do + column do + semantic_form_for :score_exports, :url => admin_score_exports_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." + 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 running 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 - f.buttons end + #panel "Upaid Registrations" do # table_for Registration.unpaid.limit(10).order('created_at desc') do # column "Registration" do |registration| diff --git a/db/manifest b/db/manifest index 65fbf8688..e3db60d29 100755 --- a/db/manifest +++ b/db/manifest @@ -195,4 +195,5 @@ max_mind_releases.sql score_histories.sql update_sms_index.sql connection_allow_null_locidispid.sql -track_user_in_scores.sql \ No newline at end of file +track_user_in_scores.sql +median_aggregate.sql \ No newline at end of file diff --git a/db/up/median_aggregate.sql b/db/up/median_aggregate.sql new file mode 100644 index 000000000..63bf77d96 --- /dev/null +++ b/db/up/median_aggregate.sql @@ -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='{}' +); From 2716211b18c443f30ad4818db9dea78e583beb77 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 27 Jul 2014 11:49:26 -0500 Subject: [PATCH 03/10] * VRFS-1496 fixing up help text and header text --- admin/app/admin/score_export.rb | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/admin/app/admin/score_export.rb b/admin/app/admin/score_export.rb index 5e4802a0d..85d1aac0c 100644 --- a/admin/app/admin/score_export.rb +++ b/admin/app/admin/score_export.rb @@ -1,4 +1,4 @@ -ActiveAdmin.register_page "Score Exports" do +ActiveAdmin.register_page "Download CSV" do menu :parent => 'Score' page_action :create_csv, :method => :post do @@ -18,20 +18,18 @@ ActiveAdmin.register_page "Score Exports" do end_time = "#{end_time}" end - puts "start_time #{start_time}, end_time #{end_time}" - 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) + .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| @@ -52,10 +50,10 @@ ActiveAdmin.register_page "Score Exports" do end - content :title => "Score Exports" do + content :title => "Export Score" do columns do column do - semantic_form_for :score_exports, :url => admin_score_exports_create_csv_path, :builder => ActiveAdmin::FormBuilder do |f| + 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 @@ -78,7 +76,7 @@ ActiveAdmin.register_page "Score Exports" 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 running 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 + 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 From c65b05993b5cbb59712d521e4cecffe020fcafbe Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 27 Jul 2014 12:22:35 -0500 Subject: [PATCH 04/10] * adding migrate script to admin --- admin/migrate.sh | 2 ++ 1 file changed, 2 insertions(+) create mode 100755 admin/migrate.sh diff --git a/admin/migrate.sh b/admin/migrate.sh new file mode 100755 index 000000000..a9afa1578 --- /dev/null +++ b/admin/migrate.sh @@ -0,0 +1,2 @@ +#!/bin/bash +bundle exec jam_db up --connopts=dbname:jam host:localhost user:postgres password:postgres --verbose From 750bceec2e5f8a0aec1d40a93b347ce46a517b8b Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 27 Jul 2014 12:23:15 -0500 Subject: [PATCH 05/10] * turn on more debugging for build server --- web/build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/build b/web/build index 622605d89..40175dcf2 100755 --- a/web/build +++ b/web/build @@ -1,7 +1,7 @@ #!/bin/bash set -e - +set -x DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # 'target' is the output directory From 2f76a82bd308145bfb822fe28ccd2996a698f1b1 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 27 Jul 2014 12:46:20 -0500 Subject: [PATCH 06/10] * fix changes to resque-scheduler includes --- web/Rakefile | 3 +++ web/app/views/clients/_web_filter.html.erb | 2 +- web/build | 1 - web/lib/tasks/scheduler.rake | 4 ++-- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/web/Rakefile b/web/Rakefile index d120b3602..f4019acfc 100644 --- a/web/Rakefile +++ b/web/Rakefile @@ -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 diff --git a/web/app/views/clients/_web_filter.html.erb b/web/app/views/clients/_web_filter.html.erb index 6baa9cfc7..a493402b5 100644 --- a/web/app/views/clients/_web_filter.html.erb +++ b/web/app/views/clients/_web_filter.html.erb @@ -16,7 +16,7 @@ <% else %> - <%= 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 %> diff --git a/web/build b/web/build index 40175dcf2..87f8dccd9 100755 --- a/web/build +++ b/web/build @@ -1,7 +1,6 @@ #!/bin/bash set -e -set -x DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # 'target' is the output directory diff --git a/web/lib/tasks/scheduler.rake b/web/lib/tasks/scheduler.rake index 55e00cfdf..099d915dc 100644 --- a/web/lib/tasks/scheduler.rake +++ b/web/lib/tasks/scheduler.rake @@ -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 From 50c611d7291ea6edf01266daccdeaa5019be7caa Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 28 Jul 2014 09:49:56 -0500 Subject: [PATCH 07/10] * removing debugger gem from admin gemfile for now --- admin/Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/Gemfile b/admin/Gemfile index fe8c1df52..751e53b9b 100644 --- a/admin/Gemfile +++ b/admin/Gemfile @@ -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' From ef87d635e262a229b92c52cf1444b61b16381216 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 28 Jul 2014 10:05:16 -0500 Subject: [PATCH 08/10] * fixing some admin pathing/url issues --- admin/app/admin/score_export.rb | 4 ++-- admin/app/admin/score_history.rb | 8 +++++++- admin/app/assets/javascripts/admin_rest.js | 2 +- admin/app/assets/javascripts/mix_again.js | 2 +- admin/config/initializers/gon.rb | 2 +- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/admin/app/admin/score_export.rb b/admin/app/admin/score_export.rb index 85d1aac0c..ddfcfaec5 100644 --- a/admin/app/admin/score_export.rb +++ b/admin/app/admin/score_export.rb @@ -65,7 +65,7 @@ ActiveAdmin.register_page "Download CSV" do end column do panel "Usage" do - span "Select a start day, and end day to generate a CSV with a score summary." + 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 @@ -75,7 +75,7 @@ ActiveAdmin.register_page "Download CSV" do 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 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 diff --git a/admin/app/admin/score_history.rb b/admin/app/admin/score_history.rb index 3fb0c5d17..02306ec43 100644 --- a/admin/app/admin/score_history.rb +++ b/admin/app/admin/score_history.rb @@ -4,10 +4,16 @@ ActiveAdmin.register JamRuby::ScoreHistory, :as => 'Score History' do config.batch_actions = false config.clear_action_items! config.filters = true - config.per_page = 10 + 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 diff --git a/admin/app/assets/javascripts/admin_rest.js b/admin/app/assets/javascripts/admin_rest.js index e41ea253a..64f49efdf 100644 --- a/admin/app/assets/javascripts/admin_rest.js +++ b/admin/app/assets/javascripts/admin_rest.js @@ -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 }); diff --git a/admin/app/assets/javascripts/mix_again.js b/admin/app/assets/javascripts/mix_again.js index 5086e7a14..0272941f9 100644 --- a/admin/app/assets/javascripts/mix_again.js +++ b/admin/app/assets/javascripts/mix_again.js @@ -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('
Mix enqueued
Resque Web').dialog(); + $link.closest('div.mix-again').find('div.mix-again-dialog').html('
Mix enqueued
Resque Web').dialog(); }) .error(function(jqXHR) { $link.closest('div.mix-again').find('div.mix-again-dialog').html('Mix failed: ' + jqXHR.responseText).dialog(); diff --git a/admin/config/initializers/gon.rb b/admin/config/initializers/gon.rb index 9eb7ab9da..d8e7bd43f 100644 --- a/admin/config/initializers/gon.rb +++ b/admin/config/initializers/gon.rb @@ -1 +1 @@ -Gon.global.prefix = ENV['RAILS_RELATIVE_URL_ROOT'] || '/' \ No newline at end of file +Gon.global.prefix = ENV['RAILS_RELATIVE_URL_ROOT'] || '' \ No newline at end of file From e9a4f33420e2d0527c6ecfc0b0a6a8e91c3648e7 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Mon, 28 Jul 2014 17:37:47 -0500 Subject: [PATCH 09/10] * VRFS-1957 - use median instead of just most recent for current_scores --- db/manifest | 3 +- db/up/current_scores_use_median.sql | 13 + ruby/lib/jam_ruby/models/search.rb | 2 +- ruby/spec/jam_ruby/models/score_spec.rb | 426 ++++++++++++++++-------- 4 files changed, 311 insertions(+), 133 deletions(-) create mode 100644 db/up/current_scores_use_median.sql diff --git a/db/manifest b/db/manifest index e3db60d29..e23c3243d 100755 --- a/db/manifest +++ b/db/manifest @@ -196,4 +196,5 @@ score_histories.sql update_sms_index.sql connection_allow_null_locidispid.sql track_user_in_scores.sql -median_aggregate.sql \ No newline at end of file +median_aggregate.sql +current_scores_use_median.sql \ No newline at end of file diff --git a/db/up/current_scores_use_median.sql b/db/up/current_scores_use_median.sql new file mode 100644 index 000000000..44a02e0f8 --- /dev/null +++ b/db/up/current_scores_use_median.sql @@ -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; diff --git a/ruby/lib/jam_ruby/models/search.rb b/ruby/lib/jam_ruby/models/search.rb index 4b1d1868b..409d0744a 100644 --- a/ruby/lib/jam_ruby/models/search.rb +++ b/ruby/lib/jam_ruby/models/search.rb @@ -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]) diff --git a/ruby/spec/jam_ruby/models/score_spec.rb b/ruby/spec/jam_ruby/models/score_spec.rb index c360cf881..195642167 100644 --- a/ruby/spec/jam_ruby/models/score_spec.rb +++ b/ruby/spec/jam_ruby/models/score_spec.rb @@ -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 From c28e932c2d840c7f8aa9c170a988308f46f3274c Mon Sep 17 00:00:00 2001 From: Anthony Davis Date: Mon, 28 Jul 2014 21:14:09 -0500 Subject: [PATCH 10/10] VRFS-1933 - integration tests for all ways of creating a session - might fail on Jenkins --- web/spec/features/create_session_flow_spec.rb | 49 +--- web/spec/features/create_session_spec.rb | 231 ++++++++++++++++++ web/spec/support/utilities.rb | 45 ++-- 3 files changed, 264 insertions(+), 61 deletions(-) create mode 100644 web/spec/features/create_session_spec.rb diff --git a/web/spec/features/create_session_flow_spec.rb b/web/spec/features/create_session_flow_spec.rb index f8e672f0e..d61b9f213 100644 --- a/web/spec/features/create_session_flow_spec.rb +++ b/web/spec/features/create_session_flow_spec.rb @@ -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 \ No newline at end of file diff --git a/web/spec/features/create_session_spec.rb b/web/spec/features/create_session_spec.rb new file mode 100644 index 000000000..85a839916 --- /dev/null +++ b/web/spec/features/create_session_spec.rb @@ -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 \ No newline at end of file diff --git a/web/spec/support/utilities.rb b/web/spec/support/utilities.rb index 3fa667adb..27b50ad35 100644 --- a/web/spec/support/utilities.rb +++ b/web/spec/support/utilities.rb @@ -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