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' diff --git a/admin/app/admin/score_export.rb b/admin/app/admin/score_export.rb new file mode 100644 index 000000000..2b223a531 --- /dev/null +++ b/admin/app/admin/score_export.rb @@ -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).strftime('%F') + 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 \ No newline at end of file diff --git a/admin/app/admin/score_history.rb b/admin/app/admin/score_history.rb index ccd827eac..85ed34393 100644 --- a/admin/app/admin/score_history.rb +++ b/admin/app/admin/score_history.rb @@ -2,36 +2,81 @@ ActiveAdmin.register JamRuby::ScoreHistory, :as => 'Score History' do menu :parent => 'Score' config.batch_actions = false + config.sort_order = 'score_dt_desc' 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 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 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 diff --git a/db/manifest b/db/manifest index 65fbf8688..8bad4f17c 100755 --- a/db/manifest +++ b/db/manifest @@ -195,4 +195,8 @@ 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 +current_scores_use_median.sql +current_scores_ams_index_sms_index_use_user_instrument.sql +locidispid_in_score_histories.sql \ No newline at end of file diff --git a/db/up/current_scores_ams_index_sms_index_use_user_instrument.sql b/db/up/current_scores_ams_index_sms_index_use_user_instrument.sql new file mode 100644 index 000000000..a3f7b7b10 --- /dev/null +++ b/db/up/current_scores_ams_index_sms_index_use_user_instrument.sql @@ -0,0 +1,163 @@ +-- this adds the user's latency, if available + +DROP VIEW current_scores; +CREATE OR REPLACE VIEW current_scores AS + + SELECT * FROM (SELECT * , row_number() OVER (PARTITION BY alocidispid, blocidispid, scorer ORDER BY full_score DESC) AS pcnum FROM + (SELECT * FROM + (SELECT percent_rank() over (PARTITION BY alocidispid, blocidispid ORDER BY full_score ASC) AS pc, * FROM + (SELECT tmp.*, (COALESCE(a_users.last_jam_audio_latency, 13) + COALESCE(b_users.last_jam_audio_latency, 13) + tmp.score) AS full_score, a_users.last_jam_audio_latency AS a_audio_latency, b_users.last_jam_audio_latency AS b_audio_latency FROM + (SELECT *, row_number() OVER (PARTITION BY alocidispid, blocidispid ORDER BY scores.created_at DESC) AS rownum FROM scores) tmp + LEFT JOIN users as a_users ON a_users.id = tmp.auserid + LEFT JOIN users as b_users ON b_users.id = tmp.buserid + WHERE rownum < 6) AS score_ranked) + AS tmp2 WHERE pc <= .5 ORDER BY pc DESC) pcs ) + AS final WHERE pcnum < 2; + + +-- check that the music_sessions does not currently have an active_music_sessions +CREATE OR REPLACE FUNCTION sms_index (my_user_id VARCHAR, my_locidispid BIGINT, my_audio_latency INTEGER) RETURNS VOID STRICT VOLATILE AS $$ + BEGIN + -- output table to hold tagged music sessions with latency + CREATE TEMPORARY TABLE sms_music_session_tmp (music_session_id VARCHAR(64) NOT NULL, tag INTEGER, latency INTEGER) ON COMMIT DROP; + + -- populate sms_music_session_tmp as all music sessions + -- XXX: we should pass in enough info to match pagination/query to reduce the impact of this step + INSERT INTO sms_music_session_tmp SELECT DISTINCT id, NULL::INTEGER AS tag, NULL::INTEGER AS latency + FROM music_sessions + WHERE (scheduled_start IS NULL OR scheduled_start > (NOW() - (interval '15 minute'))) + AND canceled = FALSE + AND id NOT IN (SELECT id FROM active_music_sessions); + + -- tag accepted rsvp as 1 + UPDATE sms_music_session_tmp q SET tag = 1 FROM rsvp_slots s, rsvp_requests_rsvp_slots rrs, rsvp_requests r WHERE + q.music_session_id = s.music_session_id AND + s.id = rrs.rsvp_slot_id AND + rrs.rsvp_request_id = r.id AND + r.user_id = my_user_id AND + rrs.chosen = TRUE AND + q.tag is NULL; + + -- tag invitation as 2 + UPDATE sms_music_session_tmp q SET tag = 2 FROM invitations i WHERE + q.music_session_id = i.music_session_id AND + i.receiver_id = my_user_id AND + q.tag IS NULL; + + -- musician access as 3 + UPDATE sms_music_session_tmp q SET tag = 3 FROM music_sessions m WHERE + q.music_session_id = m.id AND + m.open_rsvps = TRUE AND + q.tag IS NULL; + + -- delete anything not tagged + DELETE FROM sms_music_session_tmp WHERE tag IS NULL; + + -- output table to hold users involved in the sms_music_session_tmp sessions and their latency + CREATE TEMPORARY TABLE sms_users_tmp (music_session_id VARCHAR(64), user_id VARCHAR(64) NOT NULL, latency INTEGER) ON COMMIT DROP; + + IF my_audio_latency > -1 THEN + -- populate sms_users_tmp with users that have an approved RSVP for sessions in the sms_music_session_tmp table, accompanied with full latency and music session + INSERT INTO sms_users_tmp SELECT q.music_session_id, users.id, s.full_score/2 AS latency + FROM sms_music_session_tmp q + INNER JOIN rsvp_slots ON rsvp_slots.music_session_id = q.music_session_id + INNER JOIN rsvp_requests_rsvp_slots ON rsvp_requests_rsvp_slots.rsvp_slot_id = rsvp_slots.id + INNER JOIN rsvp_requests ON rsvp_requests.id = rsvp_requests_rsvp_slots.rsvp_request_id + INNER JOIN users ON rsvp_requests.user_id = users.id + LEFT OUTER JOIN current_scores s ON s.alocidispid = users.last_jam_locidispid + WHERE + s.blocidispid = my_locidispid AND + rsvp_requests_rsvp_slots.chosen = TRUE; + + -- populate sms_users_tmp with invited users for session in the sms_music_session_tmp table, accompanied with full latency and music session + -- specify NULL for music_session_id, because we don't want RSVP users to affect the AVG computed for each session later + INSERT INTO sms_users_tmp SELECT NULL, users.id, s.full_score/2 AS latency + FROM sms_music_session_tmp q + INNER JOIN invitations ON invitations.music_session_id = q.music_session_id + INNER JOIN users ON invitations.receiver_id = users.id + LEFT OUTER JOIN current_scores s ON s.alocidispid = users.last_jam_locidispid + WHERE + s.blocidispid = my_locidispid AND + users.id NOT IN (SELECT user_id FROM sms_users_tmp); + END IF; + + -- calculate the average latency + UPDATE sms_music_session_tmp q SET latency = (select AVG(u.latency) FROM sms_users_tmp u WHERE + q.music_session_id = u.music_session_id); + + RETURN; + END; +$$ LANGUAGE plpgsql; + +-- my_audio_latency can have a special value of -1, which means 'unknown'. +CREATE OR REPLACE FUNCTION ams_index (my_user_id VARCHAR, my_locidispid BIGINT, my_audio_latency INTEGER) RETURNS VOID STRICT VOLATILE AS $$ + BEGIN + -- output table to hold tagged music sessions with latency + CREATE TEMPORARY TABLE ams_music_session_tmp (music_session_id VARCHAR(64) NOT NULL, tag INTEGER, latency INTEGER) ON COMMIT DROP; + + -- populate ams_music_session_tmp as all music sessions + INSERT INTO ams_music_session_tmp SELECT DISTINCT id, NULL::INTEGER AS tag, NULL::INTEGER AS latency + FROM active_music_sessions; + + -- TODO worry about active music session where my_user_id is the creator? + -- eh, maybe, but if the music session is active and you're the creator wouldn't you already be in it? + -- so maybe you're on another computer, so why care? plus seth is talking about auto rsvp'ing the session + -- for you, so maybe not a problem. + + -- tag accepted rsvp as 1 + UPDATE ams_music_session_tmp q SET tag = 1 FROM rsvp_slots s, rsvp_requests_rsvp_slots rrs, rsvp_requests r WHERE + q.music_session_id = s.music_session_id AND + s.id = rrs.rsvp_slot_id AND + rrs.rsvp_request_id = r.id AND + r.user_id = my_user_id AND + rrs.chosen = TRUE AND + q.tag is NULL; + + -- tag invitation as 2 + UPDATE ams_music_session_tmp q SET tag = 2 FROM invitations i WHERE + q.music_session_id = i.music_session_id AND + i.receiver_id = my_user_id AND + q.tag IS NULL; + + -- musician access as 3 + UPDATE ams_music_session_tmp q SET tag = 3 FROM music_sessions m WHERE + q.music_session_id = m.id AND + m.musician_access = TRUE AND + q.tag IS NULL; + + -- delete anything not tagged + DELETE FROM ams_music_session_tmp WHERE tag IS NULL; + + -- output table to hold users involved in the ams_music_session_tmp sessions and their latency + CREATE TEMPORARY TABLE ams_users_tmp (music_session_id VARCHAR(64), user_id VARCHAR(64) NOT NULL, latency INTEGER) ON COMMIT DROP; + + IF my_audio_latency > -1 THEN + -- populate ams_users_tmp with users that have a connection for sessions in the ams_music_session_tmp table, accompanied with full latency and music session + INSERT INTO ams_users_tmp SELECT c.music_session_id, c.user_id, s.full_score/2 AS latency + FROM ams_music_session_tmp q + INNER JOIN connections c ON c.music_session_id = q.music_session_id + LEFT OUTER JOIN current_scores s ON s.alocidispid = c.locidispid + WHERE s.blocidispid = my_locidispid; + + -- populate ams_users_tmp with users that have an approved RSVP for sessions inthe ams_music_session_tmp table, accompanied with full latency and music session + -- specify NULL for music_session_id, because we don't want RSVP users to affect the AVG computed for each session later + INSERT INTO ams_users_tmp SELECT NULL, users.id, s.full_score/2 AS latency + FROM ams_music_session_tmp q + INNER JOIN rsvp_slots ON rsvp_slots.music_session_id = q.music_session_id + INNER JOIN rsvp_requests_rsvp_slots ON rsvp_requests_rsvp_slots.rsvp_slot_id = rsvp_slots.id + INNER JOIN rsvp_requests ON rsvp_requests.id = rsvp_requests_rsvp_slots.rsvp_request_id + INNER JOIN users ON rsvp_requests.user_id = users.id + LEFT OUTER JOIN current_scores s ON s.alocidispid = users.last_jam_locidispid + WHERE + s.blocidispid = my_locidispid AND + rsvp_requests_rsvp_slots.chosen = TRUE AND + users.id NOT IN (SELECT user_id FROM ams_users_tmp); + END IF; + + -- calculate the average latency + UPDATE ams_music_session_tmp q SET latency = (select AVG(u.latency) FROM ams_users_tmp u WHERE + q.music_session_id = u.music_session_id); + + RETURN; + END; +$$ LANGUAGE plpgsql; \ No newline at end of file diff --git a/db/up/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/db/up/locidispid_in_score_histories.sql b/db/up/locidispid_in_score_histories.sql new file mode 100644 index 000000000..38846045b --- /dev/null +++ b/db/up/locidispid_in_score_histories.sql @@ -0,0 +1,5 @@ +-- https://jamkazam.atlassian.net/browse/VRFS-1968 +-- store both locids in score_histories + +ALTER TABLE score_histories ADD COLUMN from_locidispid BIGINT NOT NULL; +ALTER TABLE score_histories ADD COLUMN to_locidispid BIGINT NOT NULL; \ 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='{}' +); diff --git a/db/up/scores_better_test_data.sql b/db/up/scores_better_test_data.sql index 52a8e9842..0f5935cc6 100644 --- a/db/up/scores_better_test_data.sql +++ b/db/up/scores_better_test_data.sql @@ -86,10 +86,10 @@ CREATE OR REPLACE FUNCTION generate_scores_dataset () RETURNS VOID STRICT VOLATI INSERT INTO cities (city, region, countrycode) select distinct city, region, countrycode from geoiplocations where length(city) > 0 and length(countrycode) > 0; DELETE FROM regions; - INSERT INTO regions (region, countrycode) select distinct region, countrycode from cities; + INSERT INTO regions (region, regionname, countrycode) select distinct region, region, countrycode from cities; DELETE FROM countries; - INSERT INTO countries (countrycode) select distinct countrycode from regions; + INSERT INTO countries (countrycode, countryname) select distinct countrycode, countrycode from regions; END IF; RETURN; diff --git a/ruby/lib/jam_ruby/models/music_session.rb b/ruby/lib/jam_ruby/models/music_session.rb index 271bd5863..290fd93f4 100644 --- a/ruby/lib/jam_ruby/models/music_session.rb +++ b/ruby/lib/jam_ruby/models/music_session.rb @@ -111,6 +111,7 @@ module JamRuby new_slot = RsvpSlot.new new_slot.instrument_id = slot.instrument_id new_slot.proficiency_level = slot.proficiency_level + new_slot.is_unstructured_rsvp = slot.is_unstructured_rsvp new_session.rsvp_slots << new_slot # get the request for this slot that was approved (should only be ONE) @@ -409,7 +410,7 @@ module JamRuby end def has_mount? - active_music_session && active_music_session.mount + !active_music_session.nil? && !active_music_session.mount.nil? end def can_cancel? user diff --git a/ruby/lib/jam_ruby/models/rsvp_request.rb b/ruby/lib/jam_ruby/models/rsvp_request.rb index 6ba1d2ab9..3558df969 100644 --- a/ruby/lib/jam_ruby/models/rsvp_request.rb +++ b/ruby/lib/jam_ruby/models/rsvp_request.rb @@ -253,7 +253,7 @@ module JamRuby # send notification if music_session.creator.id == user.id - Notification.send_scheduled_session_rsvp_cancelled_org(music_session, user) + Notification.send_scheduled_session_rsvp_cancelled_org(music_session, rsvp_request.user) else Notification.send_scheduled_session_rsvp_cancelled(music_session, user) end diff --git a/ruby/lib/jam_ruby/models/score_history.rb b/ruby/lib/jam_ruby/models/score_history.rb index 5c17640f6..4c82e3326 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 @@ -12,14 +15,15 @@ module JamRuby result = connection.execute( "INSERT INTO score_histories - (from_client_id, from_user_id, from_latency_tester_id, from_addr, from_isp, from_country, from_region, from_city, from_postal, from_latitude, from_longitude, - to_client_id, to_user_id, to_latency_tester_id, to_addr, to_isp, to_country, to_region, to_city, to_postal, to_latitude, to_longitude, + (from_client_id, from_user_id, from_latency_tester_id, from_addr, from_locidispid, from_isp, from_country, from_region, from_city, from_postal, from_latitude, from_longitude, + to_client_id, to_user_id, to_latency_tester_id, to_addr, to_locidispid, to_isp, to_country, to_region, to_city, to_postal, to_latitude, to_longitude, score, score_dt, scoring_data) SELECT s.anodeid AS from_client_id, s.auserid AS from_user_id, s.alatencytestid AS from_latency_tester_id, s.aaddr AS from_addr, + s.alocidispid AS from_locidispid, x.company AS from_isp, a.countrycode AS from_country, a.region AS from_region, @@ -31,6 +35,7 @@ module JamRuby s.buserid AS to_user_id, s.blatencytestid AS to_latency_tester_id, s.baddr AS to_addr, + s.blocidispid AS to_locidispid, y.company AS to_isp, b.countrycode AS to_country, b.region AS to_region, diff --git a/ruby/lib/jam_ruby/models/search.rb b/ruby/lib/jam_ruby/models/search.rb index 4b1d1868b..761660c84 100644 --- a/ruby/lib/jam_ruby/models/search.rb +++ b/ruby/lib/jam_ruby/models/search.rb @@ -98,8 +98,11 @@ module JamRuby M_ORDER_FOLLOWS = ['Most Followed', :followed] M_ORDER_PLAYS = ['Most Plays', :plays] M_ORDER_PLAYING = ['Playing Now', :playing] - ORDERINGS = B_ORDERINGS = M_ORDERINGS = [M_ORDER_FOLLOWS, M_ORDER_PLAYS, M_ORDER_PLAYING] - B_ORDERING_KEYS = M_ORDERING_KEYS = M_ORDERINGS.collect { |oo| oo[1] } + M_ORDER_LATENCY = ['Latency To Me', :latency] + M_ORDERINGS = [M_ORDER_LATENCY, M_ORDER_FOLLOWS, M_ORDER_PLAYS] + ORDERINGS = B_ORDERINGS = [M_ORDER_FOLLOWS, M_ORDER_PLAYS, M_ORDER_PLAYING] + M_ORDERING_KEYS = M_ORDERINGS.collect { |oo| oo[1] } + B_ORDERING_KEYS = B_ORDERINGS.collect { |oo| oo[1] } DISTANCE_OPTS = B_DISTANCE_OPTS = M_DISTANCE_OPTS = [['Any', 0], [1000.to_s, 1000], [500.to_s, 500], [250.to_s, 250], [100.to_s, 100], [50.to_s, 50], [25.to_s, 25]] @@ -150,7 +153,7 @@ module JamRuby # puts "================ user #{user.inspect}" # puts "================ conn #{conn.inspect}" - rel = User.musicians_geocoded + rel = User.musicians # not musicians_geocoded on purpose; we allow 'unknowns' to surface in the search page rel = rel.select('users.*') rel = rel.group('users.id') @@ -164,7 +167,7 @@ module JamRuby # filter on scores using selections from params # see M_SCORE_OPTS - score_limit = TEST_SCORE + score_limit = ANY_SCORE l = params[:score_limit] unless l.nil? score_limit = l @@ -182,6 +185,7 @@ module JamRuby score_join = 'left outer' # or 'inner' score_min = nil score_max = nil + # these score_min, score_max come from here (doubled): https://jamkazam.atlassian.net/browse/VRFS-1962 case score_limit when GOOD_SCORE score_join = 'inner' @@ -190,14 +194,14 @@ module JamRuby when MODERATE_SCORE score_join = 'inner' score_min = 40 - score_max = 80 + score_max = 70 when POOR_SCORE score_join = 'inner' score_min = 80 - score_max = 120 + score_max = 100 when UNACCEPTABLE_SCORE score_join = 'inner' - score_min = 120 + score_min = 100 score_max = nil when SCORED_SCORE score_join = 'inner' @@ -216,16 +220,20 @@ module JamRuby rel = rel.joins("#{score_join} join current_scores on current_scores.alocidispid = users.last_jam_locidispid") .where(['(current_scores.blocidispid = ? or current_scores.blocidispid is null)', locidispid]) - rel = rel.where(['current_scores.score > ?', score_min]) unless score_min.nil? - rel = rel.where(['current_scores.score <= ?', score_max]) unless score_max.nil? + rel = rel.joins('LEFT JOIN regions ON regions.countrycode = users.country AND regions.region = users.state') - rel = rel.select('current_scores.score') - rel = rel.group('current_scores.score') + rel = rel.where(['current_scores.full_score > ?', score_min]) unless score_min.nil? + rel = rel.where(['current_scores.full_score <= ?', score_max]) unless score_max.nil? + + rel = rel.select('current_scores.full_score, current_scores.score, current_scores.b_audio_latency as audio_latency, regions.regionname') + rel = rel.group('current_scores.full_score, current_scores.score, current_scores.b_audio_latency, regions.regionname') end ordering = self.order_param(params) # puts "================ ordering #{ordering}" case ordering + when :latency + # nothing to do. the sort added below 'current_scores.score ASC NULLS LAST' handles this when :plays # FIXME: double counting? # sel_str = "COUNT(records)+COUNT(sessions) AS play_count, #{sel_str}" rel = rel.select('COUNT(records.id)+COUNT(sessions.id) AS search_play_count') @@ -242,7 +250,7 @@ module JamRuby end unless locidispid.nil? - rel = rel.order('current_scores.score ASC NULLS LAST') + rel = rel.order('current_scores.full_score ASC NULLS LAST') end rel = rel.order('users.created_at DESC') @@ -386,15 +394,15 @@ module JamRuby # an offline process and thus uses the last jam location as "home base" locidispid = usr.last_jam_locidispid - score_limit = 60 + score_limit = 70 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]) - .order('current_scores.score') # best scores first + .where(['current_scores.full_score <= ?', score_limit]) + .order('current_scores.full_score') # best scores first .order('users.created_at DESC') # then most recent .limit(limit) @@ -418,7 +426,7 @@ module JamRuby rel = GeoIpLocations.where_latlng(rel, params, current_user) sel_str = 'bands.*' - case ordering = self.order_param(params) + case ordering = self.order_param(params, B_ORDERING_KEYS) when :plays # FIXME: double counting? sel_str = "COUNT(records)+COUNT(msh) AS play_count, #{sel_str}" rel = rel.joins("LEFT JOIN music_sessions AS msh ON msh.band_id = bands.id") 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 diff --git a/ruby/lib/jam_ruby/resque/scheduled/music_session_scheduler.rb b/ruby/lib/jam_ruby/resque/scheduled/music_session_scheduler.rb index f54215896..cd706285a 100644 --- a/ruby/lib/jam_ruby/resque/scheduled/music_session_scheduler.rb +++ b/ruby/lib/jam_ruby/resque/scheduled/music_session_scheduler.rb @@ -27,8 +27,8 @@ module JamRuby end def run - # get all weekly sessions that have ended in the last 15 minutes - criteria = "recurring_mode = 'weekly' AND session_removed_at is not null AND canceled = false AND next_session_scheduled = false" + # get all weekly sessions that started at least 4 hours ago + criteria = "recurring_mode = 'weekly' AND scheduled_start + interval '4hours' < NOW() AND canceled = false AND next_session_scheduled = false" MusicSession.find_each(:conditions => criteria) do |music_session| music_session.copy end diff --git a/ruby/spec/jam_ruby/models/active_music_session_spec.rb b/ruby/spec/jam_ruby/models/active_music_session_spec.rb index b59dd0b45..ab02b8281 100644 --- a/ruby/spec/jam_ruby/models/active_music_session_spec.rb +++ b/ruby/spec/jam_ruby/models/active_music_session_spec.rb @@ -354,8 +354,8 @@ describe ActiveMusicSession do user = FactoryGirl.create(:user, last_jam_locidispid: 1, last_jam_audio_latency: 5) c3 = FactoryGirl.create(:connection, user: user, locidispid: 1, last_jam_audio_latency: 5) - Score.createx(c1.locidispid, c1.client_id, c1.addr, c3.locidispid, c3.client_id, c3.addr, 20, nil) - Score.createx(c2.locidispid, c2.client_id, c2.addr, c3.locidispid, c3.client_id, c3.addr, 30, nil) + Score.createx(c1.locidispid, c1.client_id, c1.addr, c3.locidispid, c3.client_id, c3.addr, 20, nil, nil, {auserid: creator.id, buserid: user.id}) + Score.createx(c2.locidispid, c2.client_id, c2.addr, c3.locidispid, c3.client_id, c3.addr, 30, nil, nil, {auserid: creator2.id, buserid: user.id}) # make a transaction diff --git a/ruby/spec/jam_ruby/models/music_session_spec.rb b/ruby/spec/jam_ruby/models/music_session_spec.rb index da8692250..ad4cc67de 100644 --- a/ruby/spec/jam_ruby/models/music_session_spec.rb +++ b/ruby/spec/jam_ruby/models/music_session_spec.rb @@ -421,7 +421,7 @@ describe MusicSession do let(:network_score) { 20 } before(:each) do - Score.createx(conn.locidispid, conn.client_id, conn.addr, searcher_conn.locidispid, searcher_conn.client_id, searcher_conn.addr, network_score, nil) + Score.createx(conn.locidispid, conn.client_id, conn.addr, searcher_conn.locidispid, searcher_conn.client_id, searcher_conn.addr, network_score, nil, nil, {auserid: creator.id, buserid: searcher.id}) end it "no results" do @@ -499,7 +499,7 @@ describe MusicSession do FactoryGirl.create(:invitation, receiver:invitee, sender:creator, music_session: music_session) # create a score between invitee, and searcher - Score.createx(invitee.last_jam_locidispid, 'immaterial', 1, searcher_conn.locidispid, searcher_conn.client_id, searcher_conn.addr, network_score, nil) + Score.createx(invitee.last_jam_locidispid, 'immaterial', 1, searcher_conn.locidispid, searcher_conn.client_id, searcher_conn.addr, network_score, nil, nil, {auserid: invitee.id, buserid: searcher.id}) music_sessions, user_scores = sms(searcher, default_opts) music_sessions.length.should == 1 @@ -580,14 +580,16 @@ describe MusicSession do end it "searcher_1" do + + # create a bad score between searcher_1 and creator_1 (but we should still see it sort 1st because it's got an RSVP to the searcher) - Score.createx(searcher_conn_1.locidispid, searcher_conn_1.client_id, searcher_conn_1.addr, creator_conn_1.locidispid, creator_conn_1.client_id, creator_conn_1.addr, bad_network_score, nil) + Score.createx(searcher_conn_1.locidispid, searcher_conn_1.client_id, searcher_conn_1.addr, creator_conn_1.locidispid, creator_conn_1.client_id, creator_conn_1.addr, bad_network_score, nil, nil, {auserid: searcher_1.id, buserid: creator_1.id}) # create a fair score between searcher_1 and creator_2 (but we should still see it sort 2st because it's got an invitation to the searcher) - Score.createx(searcher_conn_1.locidispid, searcher_conn_1.client_id, searcher_conn_1.addr, creator_conn_2.locidispid, creator_conn_2.client_id, creator_conn_2.addr, fair_network_score, nil) + Score.createx(searcher_conn_1.locidispid, searcher_conn_1.client_id, searcher_conn_1.addr, creator_conn_2.locidispid, creator_conn_2.client_id, creator_conn_2.addr, fair_network_score, nil, nil, {auserid: searcher_1.id, buserid: creator_2.id}) # create a good score between searcher_1 and creator_3 (but we should still see it sort last because it's an open session; no affiliation with the searcher) - Score.createx(searcher_conn_1.locidispid, searcher_conn_1.client_id, searcher_conn_1.addr, creator_conn_3.locidispid, creator_conn_3.client_id, creator_conn_3.addr, good_network_score, nil) + Score.createx(searcher_conn_1.locidispid, searcher_conn_1.client_id, searcher_conn_1.addr, creator_conn_3.locidispid, creator_conn_3.client_id, creator_conn_3.addr, good_network_score, nil, nil, {auserid: searcher_1.id, buserid: creator_3.id}) music_sessions, user_scores = sms(searcher_1, {client_id: searcher_conn_1.client_id}) diff --git a/ruby/spec/jam_ruby/models/musician_search_spec.rb b/ruby/spec/jam_ruby/models/musician_search_spec.rb index a98985b17..f1cafa067 100644 --- a/ruby/spec/jam_ruby/models/musician_search_spec.rb +++ b/ruby/spec/jam_ruby/models/musician_search_spec.rb @@ -30,7 +30,10 @@ describe 'Musician search' do @geomusicians << @user4 @geomusicians << @user5 - Score.delete_all + # from these scores: + # user1 has scores other users in user1 location, and with user2, user3, user4 + # user2 has scores with users in user3 and user4 location + # Score.delete_all Score.createx(1, 'a', 1, 1, 'a', 1, 10) Score.createx(1, 'a', 1, 2, 'b', 2, 20) Score.createx(1, 'a', 1, 3, 'c', 3, 30) @@ -43,46 +46,46 @@ describe 'Musician search' do it "finds all musicians" do # expects all the musicians (geocoded) - results = Search.musician_filter + results = Search.musician_filter({score_limit: Search::TEST_SCORE}) results.search_type.should == :musicians_filter - results.results.count.should == @geomusicians.length - results.results.should eq @geomusicians.reverse + results.results.count.should == @musicians.length + results.results.should eq @musicians.reverse end it "finds all musicians page 1" do # expects all the musicians - results = Search.musician_filter({page: 1}) + results = Search.musician_filter({page: 1, score_limit: Search::TEST_SCORE}) results.search_type.should == :musicians_filter - results.results.count.should == @geomusicians.length - results.results.should eq @geomusicians.reverse + results.results.count.should == @musicians.length + results.results.should eq @musicians.reverse end it "finds all musicians page 2" do # expects no musicians (all fit on page 1) - results = Search.musician_filter({page: 2}) + results = Search.musician_filter({page: 2, score_limit: Search::TEST_SCORE}) results.search_type.should == :musicians_filter results.results.count.should == 0 end it "finds all musicians page 1 per_page 3" do # expects three of the musicians - results = Search.musician_filter({per_page: 3}) + results = Search.musician_filter({per_page: 3, score_limit: Search::TEST_SCORE}) results.search_type.should == :musicians_filter results.results.count.should == 3 - results.results.should eq @geomusicians.reverse.slice(0, 3) + results.results.should eq @musicians.reverse.slice(0, 3) end it "finds all musicians page 2 per_page 3" do # expects two of the musicians - results = Search.musician_filter({page: 2, per_page: 3}) + results = Search.musician_filter({page: 2, per_page: 3, score_limit: Search::TEST_SCORE}) results.search_type.should == :musicians_filter - results.results.count.should == 2 - results.results.should eq @geomusicians.reverse.slice(3, 3) + results.results.count.should == 3 + results.results.should eq @musicians.reverse.slice(3, 3) end it "finds all musicians page 3 per_page 3" do # expects two of the musicians - results = Search.musician_filter({page: 3, per_page: 3}) + results = Search.musician_filter({page: 3, per_page: 3, score_limit: Search::TEST_SCORE}) results.search_type.should == :musicians_filter results.results.count.should == 0 end @@ -146,7 +149,7 @@ describe 'Musician search' do f3.save # @user2.followers.concat([@user3, @user4, @user2]) - results = Search.musician_filter({ :per_page => @musicians.size }, @user3) + results = Search.musician_filter({ :per_page => @musicians.size, score_limit: Search::TEST_SCORE, orderby: 'followed'}, @user3) expect(results.results[0].id).to eq(@user2.id) # check the follower count for given entry @@ -157,7 +160,7 @@ describe 'Musician search' do it 'paginates properly' do # make sure pagination works right - params = { :per_page => 2, :page => 1 } + params = { :per_page => 2, :page => 1 , score_limit: Search::TEST_SCORE} results = Search.musician_filter(params) expect(results.results.count).to be 2 end @@ -218,7 +221,7 @@ describe 'Musician search' do # create friendship record Friendship.save(@user1.id, @user2.id) # search on user2 - results = Search.musician_filter({}, @user2) + results = Search.musician_filter({score_limit: Search::TEST_SCORE}, @user2) friend = results.results.detect { |mm| mm.id == @user1.id } expect(friend).to_not be_nil expect(results.friend_count(friend)).to be 1 @@ -239,7 +242,7 @@ describe 'Musician search' do expect(recording.claimed_recordings.length).to be 1 expect(@user1.recordings.detect { |rr| rr == recording }).to_not be_nil - results = Search.musician_filter({},@user1) + results = Search.musician_filter({score_limit: Search::TEST_SCORE},@user1) # puts "====================== results #{results.inspect}" uu = results.results.detect { |mm| mm.id == @user1.id } expect(uu).to_not be_nil @@ -257,7 +260,7 @@ describe 'Musician search' do make_recording(@user1) # order results by num recordings - results = Search.musician_filter({ :orderby => 'plays' }, @user2) + results = Search.musician_filter({ orderby: 'plays', score_limit: Search::TEST_SCORE}, @user2) # puts "========= results #{results.inspect}" expect(results.results.length).to eq(2) expect(results.results[0].id).to eq(@user1.id) @@ -266,23 +269,24 @@ describe 'Musician search' do # add more data and make sure order still correct make_recording(@user3) make_recording(@user3) - results = Search.musician_filter({ :orderby => 'plays' }, @user2) + results = Search.musician_filter({ :orderby => 'plays', score_limit: Search::TEST_SCORE }, @user2) expect(results.results.length).to eq(2) expect(results.results[0].id).to eq(@user3.id) expect(results.results[1].id).to eq(@user1.id) end it "by now playing" do + pending "these tests worked, so leaving them in, but we don't currently have 'Now Playing' in the find musicians screen" # should get 1 result with 1 active session make_session(@user1) - results = Search.musician_filter({ :orderby => 'playing' }, @user2) + results = Search.musician_filter({ :orderby => 'playing', score_limit: Search::TEST_SCORE}, @user2) expect(results.results.count).to be 1 expect(results.results.first.id).to eq(@user1.id) # should get 2 results with 2 active sessions # sort order should be created_at DESC make_session(@user3) - results = Search.musician_filter({ :orderby => 'playing' }, @user2) + results = Search.musician_filter({ :orderby => 'playing', score_limit: Search::TEST_SCORE}, @user2) expect(results.results.count).to be 2 expect(results.results[0].id).to eq(@user3.id) expect(results.results[1].id).to eq(@user1.id) @@ -299,7 +303,7 @@ describe 'Musician search' do @user1.reload ii = @user1.instruments.detect { |inst| inst.id == 'tuba' } expect(ii).to_not be_nil - results = Search.musician_filter({ :instrument => ii.id }) + results = Search.musician_filter({ :instrument => ii.id, score_limit: Search::TEST_SCORE }) results.results.each do |rr| expect(rr.instruments.detect { |inst| inst.id=='tuba' }.id).to eq(ii.id) end @@ -313,13 +317,13 @@ describe 'Musician search' do # short distance results = Search.musician_filter({ :per_page => num, :distance => 10, - :city => 'Apex' }, @user1) + :city => 'Apex', score_limit: Search::TEST_SCORE }, @user1) expect(results.results.count).to be num # long distance results = Search.musician_filter({ :per_page => num, :distance => 1000, :city => 'Miami', - :state => 'FL' }, @user1) + :state => 'FL', score_limit: Search::TEST_SCORE }, @user1) expect(results.results.count).to be num end @@ -327,14 +331,14 @@ describe 'Musician search' do pending 'distance search changes' expect(@user1.lat).to_not be_nil # uses the location of @user1 - results = Search.musician_filter({ :distance => 10, :per_page => User.musicians.count }, @user1) + results = Search.musician_filter({ :distance => 10, :per_page => User.musicians.count, score_limit: Search::TEST_SCORE }, @user1) expect(results.results.count).to be User.musicians.count end it "finds no musicians within a given distance of location" do pending 'distance search changes' expect(@user1.lat).to_not be_nil - results = Search.musician_filter({ :distance => 10, :city => 'San Francisco' }, @user1) + results = Search.musician_filter({ :distance => 10, :city => 'San Francisco', score_limit: Search::TEST_SCORE }, @user1) expect(results.results.count).to be 0 end diff --git a/ruby/spec/jam_ruby/models/score_history_spec.rb b/ruby/spec/jam_ruby/models/score_history_spec.rb index 67fcdae11..8c16dfc0c 100644 --- a/ruby/spec/jam_ruby/models/score_history_spec.rb +++ b/ruby/spec/jam_ruby/models/score_history_spec.rb @@ -38,6 +38,7 @@ describe ScoreHistory do score_history.from_user_id.should == score1.auserid score_history.from_latency_tester_id.should == score1.alatencytestid score_history.from_addr.should == score1.aaddr + score_history.from_locidispid.should == score1.alocidispid score_history.from_isp.should == austin[:jamisp].jam_company.company score_history.from_country.should == austin[:geoiplocation].countrycode score_history.from_region.should == austin[:geoiplocation].region @@ -49,6 +50,7 @@ describe ScoreHistory do score_history.to_user_id.should == score1.buserid score_history.to_latency_tester_id.should == score1.blatencytestid score_history.to_addr.should == score1.baddr + score_history.to_locidispid.should == score1.blocidispid score_history.to_isp.should == dallas[:jamisp].jam_company.company score_history.to_country.should == dallas[:geoiplocation].countrycode score_history.to_region.should == dallas[:geoiplocation].region 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 diff --git a/ruby/spec/jam_ruby/resque/music_session_scheduler_spec.rb b/ruby/spec/jam_ruby/resque/music_session_scheduler_spec.rb index ac4d95146..af872943b 100644 --- a/ruby/spec/jam_ruby/resque/music_session_scheduler_spec.rb +++ b/ruby/spec/jam_ruby/resque/music_session_scheduler_spec.rb @@ -18,8 +18,8 @@ describe "MusicSessionScheduler" do MusicSession.all.count.should == 1 - # end the session - ms.session_removed_at = Time.now + # move the session back more than 4 hours + ms.scheduled_start = Time.now - 5.hours ms.save! # run the scheduler again @@ -27,7 +27,6 @@ describe "MusicSessionScheduler" do MusicSession.all.count.should == 2 - MusicSession.where(:session_removed_at => nil).count.should == 1 MusicSession.where(:next_session_scheduled => true).count.should == 1 end @@ -40,7 +39,7 @@ describe "MusicSessionScheduler" do @scheduler.run MusicSession.all.count.should == 1 - ms.session_removed_at = Time.now + ms.scheduled_start = Time.now - 3.hours ms.save! @scheduler.run 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/assets/javascripts/accounts_session_detail.js b/web/app/assets/javascripts/accounts_session_detail.js index 76f9b2401..a148f4241 100644 --- a/web/app/assets/javascripts/accounts_session_detail.js +++ b/web/app/assets/javascripts/accounts_session_detail.js @@ -21,6 +21,8 @@ var $templateOpenSlots = null; var instrument_logo_map = context.JK.getInstrumentIconMap24(); var invitationDialog = null; + var inviteMusiciansUtil = null; + var friendInput=null; var LATENCY = { @@ -42,7 +44,11 @@ function inviteMusicians(e) { e.preventDefault(); - invitationDialog.showEmailDialog(); + friendInput = inviteMusiciansUtil.inviteSessionUpdate('#update-session-invite-musicians', + sessionId); + inviteMusiciansUtil.loadFriends(); + $(friendInput).show(); + // invitationDialog.showEmailDialog(); } function cancelRsvpRequest(e) { @@ -120,6 +126,7 @@ $sessionPageBtn.on('click', openSessionPage); $screen.find(".approveRsvpRequest").on('click', approveRsvpRequest); $screen.find(".declineRsvpRequest").on('click', declineRsvpRequest); + $(friendInput).focus(function() { $(this).val(''); }) $screen.find(".cancelSessionRsvp").on('click', function(e) { e.preventDefault(); @@ -393,6 +400,10 @@ $sessionDetail = $screen.find("#account-session-detail-div"); $shareUrl = $screen.find('.share-url'); invitationDialog = invitationDlg; + + inviteMusiciansUtil = new JK.InviteMusiciansUtil(JK.app); + inviteMusiciansUtil.initialize(JK.FriendSelectorDialogInstance); + $templateOpenSlots = $('#template-open-slots'); } diff --git a/web/app/assets/javascripts/dialog/rsvpSubmitDialog.js b/web/app/assets/javascripts/dialog/rsvpSubmitDialog.js index 228b0077f..895927135 100644 --- a/web/app/assets/javascripts/dialog/rsvpSubmitDialog.js +++ b/web/app/assets/javascripts/dialog/rsvpSubmitDialog.js @@ -28,11 +28,15 @@ $('.schedule-recurrence', $dialog).html("Recurs " + response.recurring_mode + " on this day at this time"); } + var hasOpenSlots = response.open_slots && response.open_slots.length > 0; + + if (response['is_unstructured_rsvp?']) { - $('.rsvp-instruments', $dialog).append('Any Instrument
'); + var checkedState = hasOpenSlots ? '' : 'checked="checked"' + $('.rsvp-instruments', $dialog).append('Play Any Instrument You Like
'); } - if (response.open_slots && response.open_slots.length > 0) { + if (hasOpenSlots) { $.each(response.open_slots, function(index, val) { var instrument = val.instrument_id; diff --git a/web/app/assets/javascripts/findMusician.js b/web/app/assets/javascripts/findMusician.js index f29dc9afb..ea8b9f564 100644 --- a/web/app/assets/javascripts/findMusician.js +++ b/web/app/assets/javascripts/findMusician.js @@ -12,6 +12,7 @@ var did_show_musician_page = false; var page_num=1, page_count=0; var textMessageDialog = null; + var $results = null; function loadMusicians(queryString) { // squelch nulls and undefines @@ -88,8 +89,8 @@ // these are raw scores as reported by client (round trip times) if (score == null) return "purple"; if (0 < score && score <= 40) return "green"; - if (40 < score && score <= 80) return "yellow"; - if (80 < score && score <= 120) return "red"; + if (40 < score && score <= 70) return "yellow"; + if (70 < score && score <= 100) return "red"; return "blue"; } @@ -97,11 +98,26 @@ // these are raw scores as reported by client (round trip times) if (score == null) return "missing"; if (0 < score && score <= 40) return "good"; - if (40 < score && score <= 80) return "moderate"; - if (80 < score && score <= 120) return "poor"; + if (40 < score && score <= 70) return "moderate"; + if (70 < score && score <= 100) return "poor"; return "unacceptable"; } + function formatLocation(musician) { + if(musician.city && musician.state) { + return musician.city + ', ' + musician.regionname + } + else if(musician.city) { + return musician.city + } + else if(musician.state) { + return musician.regionname + } + else { + return 'Location Unavailable' + } + } + function renderMusicians() { var ii, len; var mTemplate = $('#template-find-musician-row').html(); @@ -110,6 +126,7 @@ var mVals, musician, renderings=''; var instr_logos, instr; var follows, followVals, aFollow; + var myAudioLatency = musicianList.my_audio_latency; for (ii=0, len=musicians.length; ii < len; ii++) { musician = musicians[ii]; @@ -149,12 +166,12 @@ }; var musician_actions = context.JK.fillTemplate(aTemplate, actionVals); - var joined_score = musician['joined_score'] + var full_score = musician['full_score']; mVals = { avatar_url: context.JK.resolveAvatarUrl(musician.photo_url), profile_url: "/client#/profile/" + musician.id, musician_name: musician.name, - musician_location: musician.city + ', ' + musician.state, + musician_location: formatLocation(musician), instruments: instr_logos, biography: musician['biography'], follow_count: musician['follow_count'], @@ -164,14 +181,27 @@ musician_id: musician['id'], musician_follow_template: follows, musician_action_template: musician_actions, - musician_one_way_score: score_to_text(joined_score), - musician_score_color: score_to_color(joined_score), - musician_score_color_alt: score_to_color_alt(joined_score) + musician_one_way_score: score_to_text(full_score), + musician_score_color: score_to_color(full_score), + musician_score_color_alt: score_to_color_alt(full_score) + }; - var musician_row = context.JK.fillTemplate(mTemplate, mVals); - renderings += musician_row; + var $rendering = $(context.JK.fillTemplate(mTemplate, mVals)) + + var $offsetParent = $results.closest('.content'); + var options = {positions: ['top', 'bottom', 'right', 'left'], offsetParent: $offsetParent}; + var scoreOptions = {positions: ['right', 'top', 'bottom', 'left'], offsetParent: $offsetParent, width:'600px'}; + context.JK.helpBubble($('.follower-count', $rendering), 'musician-follower-count', {}, options); + context.JK.helpBubble($('.friend-count', $rendering), 'musician-friend-count', {}, options); + context.JK.helpBubble($('.recording-count', $rendering), 'musician-recording-count', {}, options); + context.JK.helpBubble($('.session-count', $rendering), 'musician-session-count', {}, options); + context.JK.helpBubble($('.score-count', $rendering), 'musician-score-count', + {full_score: full_score ? Math.round(full_score / 2) : null, my_gear_latency: myAudioLatency, their_gear_latency:musician['audio_latency'], internet_latency: musician['score']}, + scoreOptions) + + $results.append($rendering); } - $('#musician-filter-results').append(renderings); + $('.search-m-friend').on('click', friendMusician); $('.search-m-follow').on('click', followMusician); @@ -279,6 +309,8 @@ }; app.bindScreen('musicians', screenBindings); + $results = $('#musician-filter-results'); + events(); } diff --git a/web/app/assets/javascripts/inviteMusicians.js b/web/app/assets/javascripts/inviteMusicians.js index 604641e1d..60d60b067 100644 --- a/web/app/assets/javascripts/inviteMusicians.js +++ b/web/app/assets/javascripts/inviteMusicians.js @@ -59,6 +59,8 @@ addInvitation(dd.name, dd.id); }); }).fail(app.ajaxError); + + return friendInput; } this.clearSelections = function() { @@ -133,7 +135,7 @@ } else { $(friendInput).select(); - context.alert('Invitation already exists for this musician.'); + // context.alert('Invitation already exists for this musician.'); } } diff --git a/web/app/assets/javascripts/notificationPanel.js b/web/app/assets/javascripts/notificationPanel.js index d16b674b0..2c50316de 100644 --- a/web/app/assets/javascripts/notificationPanel.js +++ b/web/app/assets/javascripts/notificationPanel.js @@ -309,10 +309,15 @@ } var $action_btn = $notification.find($btnNotificationAction); - $action_btn.text(actionText); - $action_btn.click(function() { - callback(payload); - }); + if (actionText === '') { + $action_btn.hide(); + } + else { + $action_btn.text(actionText); + $action_btn.click(function() { + callback(payload); + }); + } } else if (type === context.JK.MessageType.MUSICIAN_RECORDING_SAVED || type === context.JK.MessageType.BAND_RECORDING_SAVED) { diff --git a/web/app/assets/javascripts/scheduled_session.js b/web/app/assets/javascripts/scheduled_session.js index 63c9713b4..7b7de7843 100644 --- a/web/app/assets/javascripts/scheduled_session.js +++ b/web/app/assets/javascripts/scheduled_session.js @@ -117,14 +117,14 @@ var firstSession = function() { var $firstSession = $scheduledSessions.children().first().find('input[name="scheduled-session-info"]'); $firstSession.attr('checked', 'checked'); - createSessionSettings.selectedSessionId = $firstSession.attr('id'); + createSessionSettings.selectedSessionId = $firstSession.attr('data-session-id'); }; if (createSessionSettings.selectedSessionId == null) { firstSession(); } else { - var $selectedSession = $scheduledSessions.children().first().find('input[name="scheduled-session-info"][id="' + createSessionSettings.selectedSessionId + '"]'); + var $selectedSession = $scheduledSessions.children().first().find('input[name="scheduled-session-info"][data-session-id="' + createSessionSettings.selectedSessionId + '"]'); if ($selectedSession.length) $selectedSession.attr('checked', 'checked'); else @@ -349,6 +349,7 @@ function beforeMoveStep1() { if (createSessionSettings.createType == 'start-scheduled') { + createSessionSettings.selectedSessionId = $scheduledSessions.find('.iradio_minimal.checked input[name="scheduled-session-info"]').attr('data-session-id'); var session = scheduledSessions[createSessionSettings.selectedSessionId]; if(session == null) { @@ -382,7 +383,7 @@ var startTime = new Date(session.scheduled_start); var diffTime = startTime.getTime() - currentTime.getTime(); if (diffTime > ONE_HOUR) { - var confirmDialog = new context.JK.ConfirmDialog(app, "Start Session Now", + var confirmDialog = new context.JK.ConfirmDialog(app, "START SESSION NOW", "You are starting a session that is scheduled to begin more than one hour from now. Are you sure you want to do this?", "Future Session", moveToFinish); confirmDialog.initialize(); @@ -416,7 +417,7 @@ createSessionSettings.startTime = $startTimeList.val(); createSessionSettings.endTime = $endTimeList.val(); createSessionSettings.notations = []; - createSessionSettings.selectedSessionId = $scheduledSessions.find('input[name="scheduled-session-info"][checked="checked"]').attr('id'); + createSessionSettings.selectedSessionId = $scheduledSessions.find('.iradio_minimal.checked input[name="scheduled-session-info"]').attr('data-session-id'); createSessionSettings.timezone.value = $timezoneList.val(); createSessionSettings.timezone.label = $timezoneList.get(0).options[$timezoneList.get(0).selectedIndex].text; createSessionSettings.recurring_mode.label = $recurringModeList.get(0).options[$recurringModeList.get(0).selectedIndex].text; diff --git a/web/app/assets/javascripts/session.js b/web/app/assets/javascripts/session.js index 323a0ee84..25a5e0915 100644 --- a/web/app/assets/javascripts/session.js +++ b/web/app/assets/javascripts/session.js @@ -32,6 +32,7 @@ var playbackControls = null; var promptLeave = false; var rateSessionDialog = null; + var friendInput=null; var rest = context.JK.Rest(); @@ -133,6 +134,7 @@ .done(function(){ initializeSession(); }) + } function notifyWithUserInfo(title , text, clientId) { @@ -1354,7 +1356,10 @@ } function inviteMusicians() { - inviteMusiciansUtil.inviteSessionUpdate('#update-session-invite-musicians', sessionId); + friendInput = inviteMusiciansUtil.inviteSessionUpdate('#update-session-invite-musicians', + sessionId); + inviteMusiciansUtil.loadFriends(); + $(friendInput).show(); } function events() { @@ -1365,6 +1370,7 @@ $('#recording-start-stop').on('click', startStopRecording); $('#open-a-recording').on('click', openRecording); $('#session-invite-musicians').on('click', inviteMusicians); + $('#session-invite-musicians2').on('click', inviteMusicians); $('#track-settings').click(function() { configureTrackDialog.refresh(); configureTrackDialog.showVoiceChatPanel(true); @@ -1376,6 +1382,7 @@ .on('pause', onPause) .on('play', onPlay) .on('change-position', onChangePlayPosition); + $(friendInput).focus(function() { $(this).val(''); }) } this.initialize = function(localRecordingsDialogInstance, recordingFinishedDialogInstance, friendSelectorDialog) { diff --git a/web/app/assets/javascripts/sessionList.js b/web/app/assets/javascripts/sessionList.js index b471e5a1f..4321cc183 100644 --- a/web/app/assets/javascripts/sessionList.js +++ b/web/app/assets/javascripts/sessionList.js @@ -21,9 +21,9 @@ var LATENCY = { GOOD : {description: "GOOD", style: "latency-green", min: 0.0, max: 20.0}, - MEDIUM : {description: "MEDIUM", style: "latency-yellow", min: 20.0, max: 40.0}, - POOR : {description: "POOR", style: "latency-red", min: 40.0, max: 10000000000.0}, - UNREACHABLE: {description: "UNREACHABLE", style: "latency-grey", min: -1, max: -1}, + MEDIUM : {description: "MEDIUM", style: "latency-yellow", min: 20.0, max: 35.0}, + POOR : {description: "POOR", style: "latency-red", min: 35.0, max: 50}, + UNACCEPTABLE: {description: "UNACCEPTABLE", style: "latency-grey", min: 50, max: 10000000}, UNKNOWN: {description: "UNKNOWN", style: "latency-grey", min: -2, max: -2} }; @@ -350,7 +350,10 @@ } function createLatency(user) { - var latencyStyle = LATENCY.UNREACHABLE.style, latencyDescription = LATENCY.UNREACHABLE.description + + var latencyStyle; + var latencyDescription; + if (user.id === context.JK.currentUserId) { latencyStyle = LATENCY.GOOD.style, latencyDescription = LATENCY.GOOD.description; } @@ -358,24 +361,25 @@ else { var latency = user.latency; - if (!latency || latency === 1000) { - // 1000 is a magical number returned by new scoring API to indicate one or more people in the session have an unknown score + if (!latency) { latencyDescription = LATENCY.UNKNOWN.description; latencyStyle = LATENCY.UNKNOWN.style; } + else if (latency <= LATENCY.GOOD.max) { + latencyDescription = LATENCY.GOOD.description; + latencyStyle = LATENCY.GOOD.style; + } + else if (latency > LATENCY.MEDIUM.min && latency <= LATENCY.MEDIUM.max) { + latencyDescription = LATENCY.MEDIUM.description; + latencyStyle = LATENCY.MEDIUM.style; + } + else if (latency > LATENCY.POOR.min && latency <= LATENCY.UNACCEPTABLE.max) { + latencyDescription = LATENCY.POOR.description; + latencyStyle = LATENCY.POOR.style; + } else { - if (latency <= LATENCY.GOOD.max) { - latencyDescription = LATENCY.GOOD.description; - latencyStyle = LATENCY.GOOD.style; - } - else if (latency > LATENCY.MEDIUM.min && latency <= LATENCY.MEDIUM.max) { - latencyDescription = LATENCY.MEDIUM.description; - latencyStyle = LATENCY.MEDIUM.style; - } - else { - latencyDescription = LATENCY.POOR.description; - latencyStyle = LATENCY.POOR.style; - } + latencyStyle = LATENCY.UNREACHABLE.style + latencyDescription = LATENCY.UNREACHABLE.description } } diff --git a/web/app/assets/javascripts/utils.js b/web/app/assets/javascripts/utils.js index 872825c79..38785e099 100644 --- a/web/app/assets/javascripts/utils.js +++ b/web/app/assets/javascripts/utils.js @@ -106,12 +106,14 @@ if (!data) { data = {} } + if(!options) { + options = {} + } + var helpText = context._.template($('#template-help-' + templateName).html(), data, { variable: 'data' }); - var holder = $('
'); holder.append(helpText); - context.JK.hoverBubble($element, helpText, options); } @@ -183,7 +185,7 @@ cornerRadius: 0, cssStyles: { fontSize: '11px', - color: 'white', + color: '#cccccc', whiteSpace: 'normal' } }; diff --git a/web/app/assets/javascripts/web/session_info.js b/web/app/assets/javascripts/web/session_info.js index 7e8ef3dac..d95684437 100644 --- a/web/app/assets/javascripts/web/session_info.js +++ b/web/app/assets/javascripts/web/session_info.js @@ -174,7 +174,10 @@ $('.call-to-action').html('Tell the session organizer if you can no longer join this session'); $btnAction.html('CANCEL RSVP'); $btnAction.click(function(e) { - ui.launchRsvpCancelDialog(musicSessionId, rsvp.id); + ui.launchRsvpCancelDialog(musicSessionId, rsvp.id) + .one(EVENTS.RSVP_CANCELED, function() { + location.reload(); + }); }); } } @@ -184,7 +187,10 @@ $('.call-to-action').html("Tell the session organizer you'd like to play in this session"); $btnAction.html('RSVP NOW!'); $btnAction.click(function(e) { - ui.launchRsvpSubmitDialog(musicSessionId); + ui.launchRsvpSubmitDialog(musicSessionId) + .one(EVENTS.RSVP_SUBMITTED, function() { + location.reload(); + }) }); } }) @@ -201,14 +207,6 @@ }); addLatencyDetails(); - - $(document).on(EVENTS.RSVP_SUBMITTED, function() { - location.reload(); - }); - - $(document).on(EVENTS.RSVP_CANCELED, function() { - location.reload(); - }); } this.initialize = initialize; diff --git a/web/app/assets/stylesheets/client/client.css b/web/app/assets/stylesheets/client/client.css index d6325a7c0..68b4073ce 100644 --- a/web/app/assets/stylesheets/client/client.css +++ b/web/app/assets/stylesheets/client/client.css @@ -53,6 +53,7 @@ *= require ./searchResults *= require ./clientUpdate *= require ./musician + *= require ./help *= require ./jquery-ui-overrides *= require web/audioWidgets *= require web/recordings diff --git a/web/app/assets/stylesheets/client/help.css.scss b/web/app/assets/stylesheets/client/help.css.scss new file mode 100644 index 000000000..691bc7c74 --- /dev/null +++ b/web/app/assets/stylesheets/client/help.css.scss @@ -0,0 +1,43 @@ +.screen { + .bt-wrapper { + .bt-content { + + color:#cccccc; + + font-size:14px; + + p { + font-size:14px; + } + ul { + font-size:14px; + } + + li { + margin-left:1em; + margin-bottom:.5em; + } + + .definition { + font-weight:bold; + } + + .help-musician-score-count { + .measurement { + + } + + .measurement-value { + font-size:24px; + } + + .measurement-absent { + font-style:italic; + font-size:12px; + display:block; + } + } + + } + } +} \ No newline at end of file diff --git a/web/app/assets/stylesheets/client/musician.css.scss b/web/app/assets/stylesheets/client/musician.css.scss index 54a310ee9..9498cda70 100644 --- a/web/app/assets/stylesheets/client/musician.css.scss +++ b/web/app/assets/stylesheets/client/musician.css.scss @@ -24,6 +24,18 @@ } } +#musicians-screen { + + .filter-element.desc.instrument-selector { + margin-left:2px; + float:left; + + & + .dropdown-wrapper .dropdown-container { + width:150px; + } + } +} + #musician-filter-results { margin: 0 10px 0px 10px; diff --git a/web/app/assets/stylesheets/web/main.css.scss b/web/app/assets/stylesheets/web/main.css.scss index d3725237e..ab7a08f43 100644 --- a/web/app/assets/stylesheets/web/main.css.scss +++ b/web/app/assets/stylesheets/web/main.css.scss @@ -599,9 +599,6 @@ strong { } } -body.jam.web.welcome .no-websocket-connection { - display:none; -} -body.jam.web.register .no-websocket-connection { +body.jam.web .no-websocket-connection { display:none; } \ No newline at end of file diff --git a/web/app/controllers/users_controller.rb b/web/app/controllers/users_controller.rb index 8859fcbe4..212a8e12b 100644 --- a/web/app/controllers/users_controller.rb +++ b/web/app/controllers/users_controller.rb @@ -228,9 +228,6 @@ class UsersController < ApplicationController if !@user.nil? && !@user.errors.any? UserMailer.welcome_message(@user).deliver - sign_in @user - redirect_to :client - return elsif !@user.nil? # new user with validation errors; logger.debug("#{@user} has errors. can not sign in until remedied. #{@user.errors.inspect}") @@ -463,6 +460,9 @@ JS end def load_location(remote_ip, location = nil) + # useful if you need to repro something on 127.0.0.1 + # remote_ip = ' 23.119.29.89' + @location = location if @location.nil? @@ -471,7 +471,6 @@ JS @location[:country] = "US" if @location[:country].nil? - # right now we only accept US signups for beta @countriesx = MaxMindManager.countries # populate regions based on current country @regions = MaxMindManager.regions(@location[:country]) diff --git a/web/app/views/api_feeds/show.rabl b/web/app/views/api_feeds/show.rabl index 140828833..e502fcbf7 100644 --- a/web/app/views/api_feeds/show.rabl +++ b/web/app/views/api_feeds/show.rabl @@ -60,7 +60,7 @@ glue :music_session do attributes :id, :name, :location, :photo_url } - child(:active_music_session => :music_session) do + child(:active_music_session => :active_music_session) do # only show mount info if fan_access is public. Eventually we'll also need to show this in other scenarios, like if invited child({:mount => :mount}, :if => lambda { |music_session| music_session.fan_access}) { attributes :id, :name, :sourced, :listeners, :bitrate, :subtype, :url diff --git a/web/app/views/api_search/index.rabl b/web/app/views/api_search/index.rabl index fd53ba9c2..e7120fca6 100644 --- a/web/app/views/api_search/index.rabl +++ b/web/app/views/api_search/index.rabl @@ -41,9 +41,13 @@ if @search.musicians_filter_search? node :page_count do |foo| @search.page_count end + + node :my_audio_latency do |user| + current_user.last_jam_audio_latency + end child(:results => :musicians) { - attributes :id, :first_name, :last_name, :name, :city, :state, :country, :email, :online, :musician, :photo_url, :biography, :joined_score + attributes :id, :first_name, :last_name, :name, :city, :state, :country, :email, :online, :musician, :photo_url, :biography, :regionname, :score, :full_score, :audio_latency node :is_friend do |musician| @search.is_friend?(musician) diff --git a/web/app/views/clients/_account_session_detail.html.haml b/web/app/views/clients/_account_session_detail.html.haml index 6f14962e8..4d6ee95c0 100644 --- a/web/app/views/clients/_account_session_detail.html.haml +++ b/web/app/views/clients/_account_session_detail.html.haml @@ -16,7 +16,7 @@ .right %a.cancel-rsvp.button-orange{href: "#"} CANCEL RSVP %a.session-detail-page.button-orange{href: "#", rel:'external'} SESSION PAGE - %a.invite-others.button-orange{href: "#"} INVITE OTHERS + %a.invite-others.button-orange{'layout-link' => 'select-invites','href' => "#"} INVITE OTHERS .clearall #account-session-detail-div @@ -160,4 +160,4 @@ %script{type: 'text/template', id: 'template-account-session-latency'} .latency{class: "{{data.latency_style}}"} - {{data.latency_text}} \ No newline at end of file + {{data.latency_text}} diff --git a/web/app/views/clients/_account_session_properties.html.haml b/web/app/views/clients/_account_session_properties.html.haml index 16d59f872..69a6364d5 100644 --- a/web/app/views/clients/_account_session_properties.html.haml +++ b/web/app/views/clients/_account_session_properties.html.haml @@ -80,7 +80,6 @@ Fan Access: .right-column %select#session-prop-fans-access - %option{value: "listen-chat-band"} Fans may listen, chat with the band %option{value: "listen-chat-each", selected: "selected"} Fans may listen, chat with each other %option{value: "no-listen-chat"} Fans may not listen to session .clearall.right-no-left @@ -95,7 +94,6 @@ %option{value: "Standard"} Standard %option{value: "Creative Commons"} Creative Commons %option{value: "Offline"} Offline - %option{value: "Jamtracks"} Jamtracks .clearall.left-column Notation Files: .right-column diff --git a/web/app/views/clients/_help.html.erb b/web/app/views/clients/_help.html.erb index 9aad16a7c..1a531a89b 100644 --- a/web/app/views/clients/_help.html.erb +++ b/web/app/views/clients/_help.html.erb @@ -40,4 +40,42 @@ + + + + + + + + + + \ No newline at end of file diff --git a/web/app/views/clients/_musicians.html.erb b/web/app/views/clients/_musicians.html.erb index 47da7d626..df1b044b4 100644 --- a/web/app/views/clients/_musicians.html.erb +++ b/web/app/views/clients/_musicians.html.erb @@ -1,5 +1,5 @@ -<%= content_tag(:div, :layout => 'screen', 'layout-id' => 'musicians', :class => "screen secondary") do -%> +<%= content_tag(:div, :layout => 'screen', 'layout-id' => 'musicians', :class => "screen secondary", id: 'musicians-screen') do -%> <%= content_tag(:div, :class => :content) do -%> <%= content_tag(:div, :class => 'content-head') do -%> <%= content_tag(:div, image_tag("content/icon_musicians.png", {:height => 19, :width => 19}), :class => 'content-icon') %> @@ -56,13 +56,13 @@
-
+
- {friend_count} friends - {follow_count} follows - {recording_count} recordings - {session_count} sessions - {musician_one_way_score} {musician_score_color_alt} score + {friend_count} friends + {follow_count} follows + {recording_count} recordings + {session_count} sessions + {musician_one_way_score} {musician_score_color_alt} score
{musician_action_template} diff --git a/web/app/views/clients/_scheduledSession.html.erb b/web/app/views/clients/_scheduledSession.html.erb index a55d8d0cb..ca05f28d1 100644 --- a/web/app/views/clients/_scheduledSession.html.erb +++ b/web/app/views/clients/_scheduledSession.html.erb @@ -456,7 +456,7 @@