diff --git a/.gitignore b/.gitignore index bb409f447..134a86d93 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,3 @@ HTML .DS_Store coverage - diff --git a/admin/Gemfile b/admin/Gemfile index a53c17f5e..df939b390 100644 --- a/admin/Gemfile +++ b/admin/Gemfile @@ -1,7 +1,7 @@ source 'http://rubygems.org' source 'https://jamjam:blueberryjam@int.jamkazam.com/gems/' -devenv = ENV["BUILD_NUMBER"].nil? # Jenkins sets a build number environment variable +devenv = ENV["BUILD_NUMBER"].nil? || ENV["TEST_WWW"] == "1" if devenv gem 'jam_db', :path=> "../db/target/ruby_package" @@ -102,7 +102,6 @@ group :development, :test do gem 'rspec-rails' gem 'guard-rspec', '0.5.5' gem 'jasmine', '1.3.1' - # gem 'pry' gem 'execjs', '1.4.0' gem 'therubyracer' #, '0.11.0beta8' gem 'factory_girl_rails', '4.1.0' @@ -114,6 +113,9 @@ end group :test do gem 'simplecov', '~> 0.7.1' gem 'simplecov-rcov' + gem 'capybara-webkit' + gem 'capybara-screenshot' + gem 'poltergeist' end gem 'pry' diff --git a/admin/app/admin/dashboard.rb b/admin/app/admin/dashboard.rb index 3a70134aa..a22643621 100644 --- a/admin/app/admin/dashboard.rb +++ b/admin/app/admin/dashboard.rb @@ -20,7 +20,7 @@ ActiveAdmin.register_page "Dashboard" do column do panel "Recent Sessions" do ul do - MusicSessionHistory.order('created_at desc').limit(5).map do |music_session| + MusicSession.order('created_at desc').limit(5).map do |music_session| li do text_node "'#{music_session.description}' created by #{User.find(music_session.user_id).name} at #{music_session.created_at}, " text_node " members: " @@ -38,7 +38,7 @@ ActiveAdmin.register_page "Dashboard" do end end end - end + end end # column do diff --git a/admin/app/admin/feeds.rb b/admin/app/admin/feeds.rb index e1f5c2913..6c5b06aad 100644 --- a/admin/app/admin/feeds.rb +++ b/admin/app/admin/feeds.rb @@ -3,36 +3,58 @@ ActiveAdmin.register_page 'Feed' do # get user information via params user_id = nil - user_id = params[:feed][:user_id] if params[:feed] - user_name = User.find(user_id).name if user_id - user_name = "All" unless user_id + user_id = params[:feed][:user_id] if params[:feed] && params[:feed][:user_id] != '' + user_name = 'All' + user_name = User.find(user_id).to_label if user_id - render :partial => 'form', :locals => {param: params} + render :partial => 'form', locals: {user_name: user_name, user_id: user_id } - offset = 0 - limit = 10 + page = (params[:page] ||= 1).to_i + per_page = 10 + offset = (page - 1) * per_page # get feed ids where_sql = '' where_sql = "where user_id = '#{user_id}'" if user_id - sql_feed_ids = "SELECT id, 'music_session_histories' as type, created_at FROM music_sessions_history #{where_sql} + sql_feed_ids = "SELECT id, 'music_sessions' as type, created_at FROM music_sessions #{where_sql} UNION ALL SELECT DISTINCT recording_id as id, 'recordings' as type, created_at FROM recorded_tracks #{where_sql} UNION ALL SELECT id, 'diagnostics' as type, created_at FROM diagnostics #{where_sql} ORDER BY created_at DESC OFFSET #{offset} - LIMIT #{limit};" + LIMIT #{per_page};" - models = [] + sql_feed_count = "SELECT COUNT(*) FROM ( + SELECT id, 'music_sessions' as type, created_at FROM music_sessions #{where_sql} + UNION ALL + SELECT DISTINCT recording_id as id, 'recordings' as type, created_at FROM recorded_tracks #{where_sql} + UNION ALL + SELECT id, 'diagnostics' as type, created_at FROM diagnostics #{where_sql} + ORDER BY created_at DESC + ) AS IDS;" + feed_count = ActiveRecord::Base.connection.execute(sql_feed_count).values[0][0].to_i id_types = ActiveRecord::Base.connection.execute(sql_feed_ids).values + + @feed_pages = WillPaginate::Collection.create(page, per_page) do |pager| + pager.total_entries = feed_count + pager.replace(id_types) + end + + div class: 'feed-pagination' do + will_paginate @feed_pages + end + + recordings = [] + sessions = [] + diagnostics = [] id_types.each do |id_and_type| - if id_and_type[1] == "music_session_histories" - models << JamRuby::MusicSessionHistory.find(id_and_type[0]) - # elsif id_and_type[1] == "recordings" - # models << JamRuby::Recording.find(id_and_type[0]) + if id_and_type[1] == "music_sessions" + sessions << JamRuby::MusicSession.find(id_and_type[0]) elsif id_and_type[1] == "recordings" - models << JamRuby::Diagnostics.find(id_and_type[0]) + recordings << JamRuby::Recording.find(id_and_type[0]) + elsif id_and_type[1] == "diagnostics" + diagnostics << JamRuby::Diagnostic.find(id_and_type[0]) else raise "Unknown type returned from feed ids" end @@ -40,22 +62,160 @@ ActiveAdmin.register_page 'Feed' do columns do column do - panel "Activity - #{user_name}" do - para id_types.inspect - ul do - models.each do |model| - li do - text_node model.inspect + panel "Music Sessions - #{user_name}" do + if sessions.count > 0 + table_for(sessions) do + column :creator do |msh| + link_to msh.creator.to_label, admin_feed_path({feed: {user_id: msh.creator.id}}) end + column :created_at do |msh| msh.created_at.strftime('%b %d %Y, %H:%M') end + column :duration do |msh| "#{msh.duration_minutes.round(2)} minutes" end + column :members do |msh| + uu = msh.unique_users + if uu.length > 0 + uu.each do |u| + span link_to u.to_label + ', ', admin_feed_path({feed: {user_id: u.id}}) + end + else + span para 'No members' + end + end + + column :band do |msh| auto_link(msh.band, msh.band.try(:name)) end + column :fan_access do |msh| msh.fan_access end + column :plays do |msh| msh.plays.count end + column :likes do |msh| msh.likes.count end + column :comments do |msh| + if msh.comment_count > 0 + text_node "(#{msh.comment_count}) " + msh.comments.each do |comment| + text_node comment.user.to_label + ', ' + end + else + span para 'No comments' + end + end + end + else + span class: 'text-center' do + para 'No session activities.' + end + end + end + + panel "Recordings - #{user_name}" do + if recordings.count > 0 + table_for(recordings) do + column :starter do |rec| + link_to rec.owner.to_label, admin_feed_path({feed: {user_id: rec.owner.id}}) + end + column :mixes do |rec| + ul do + mixes = rec.mixes + if mixes.count > 0 + mixes.each do |mix| + li do + text_node "Created At: #{mix.created_at.strftime('%b %d %Y, %H:%M')}, " + text_node "Started At: #{mix.started_at.strftime('%b %d %Y, %H:%M')}, " + text_node "Completed At: #{mix.completed_at.strftime('%b %d %Y, %H:%M')}, " + text_node "Error Count: #{mix.error_count}, " + text_node "Error Reason: #{mix.error_reason}, " + text_node "Error Detail: #{mix.error_detail}, " + text_node "Download Count: #{mix.download_count}, " + if !mix.nil? && !mix[:ogg_url].nil? + span link_to 'Download OGG', mix.sign_url(3600, 'ogg') + else + text_node 'OGG download not available' + end + if !mix.nil? && !mix[:mp3_url].nil? + span link_to 'Download MP3', mix.sign_url(3600, 'mp3') + else + text_node 'MP3 download not available' + end + end + end + else + span para 'No mixes' + end + end + end + column :recorded_tracks do |rec| + ul do + rts = rec.recorded_tracks + if rts.count > 0 + rts.each do |gt| + li do + span link_to gt.musician.to_label, admin_feed_path({feed: {user_id: gt.musician.id}}) + span ", #{gt.instrument_id}, " + span "Download Count: #{gt.download_count}, " + span "Fully uploaded: #{gt.fully_uploaded}, " + span "Upload failures: #{gt.upload_failures}, " + span "Part failures: #{gt.part_failures}, " + if gt[:url] + # span link_to 'Download', gt.sign_url(3600) + else + span 'No track available' + end + end + end + else + span para 'No recorded tracks' + end + end + end + column :claimed_recordings do |rec| + ul do + crs = rec.claimed_recordings + if crs.count > 0 + crs.each do |cr| + li do + span cr.name + span link_to cr.user.to_label, admin_feed_path({feed: {user_id: cr.user.id}}) + span ", Public: #{cr.is_public}" + end + end + else + span para 'No claimed recordings' + end + end + end + end + else + span class: 'text-center' do + para 'No recording activities.' + end + end + end + + panel "Diagnostics - #{user_name}" do + if diagnostics.count > 0 then + table_for(diagnostics) do + column :user do |d| + span link_to d.user.to_label, admin_feed_path({feed: {user_id: d.user.id}}) + end + column :created_at do |d| d.created_at.strftime('%b %d %Y, %H:%M') end + column :type + column :creator + column :data do |d| + span style: "white-space: pre;" do + if JSON.parse(d.data).all? + JSON.pretty_generate(JSON.parse(d.data)) + else + d.data + end + end + end + end + else + span class: 'text-center' do + para 'No diagnostic activities.' end end end end end - end - - controller do - def index + div class: 'feed-pagination' do + will_paginate @feed_pages end end end \ No newline at end of file diff --git a/admin/app/admin/mix.rb b/admin/app/admin/mix.rb index 16c623ca1..ade3f0d4e 100644 --- a/admin/app/admin/mix.rb +++ b/admin/app/admin/mix.rb @@ -24,8 +24,8 @@ ActiveAdmin.register JamRuby::Mix, :as => 'Mixes' do attributes_table_for(mix) do row :recording do |mix| auto_link(mix.recording, mix.recording.id) end row :created_at do |mix| mix.created_at.strftime('%b %d %Y, %H:%M') end - # row :s3_url do |mix| mix.url end - # row :manifest do |mix| mix.manifest end + row :s3_url do |mix| mix.sign_url end + row :manifest do |mix| mix.manifest end row :completed do |mix| "#{mix.completed ? "finished" : "not finished"}" end if mix.completed row :completed_at do |mix| mix.completed_at.strftime('%b %d %Y, %H:%M') end diff --git a/admin/app/admin/music_session_history.rb b/admin/app/admin/music_session_history.rb index d4fb59a1b..c7eb5fcb7 100644 --- a/admin/app/admin/music_session_history.rb +++ b/admin/app/admin/music_session_history.rb @@ -1,4 +1,4 @@ -ActiveAdmin.register JamRuby::MusicSessionHistory, :as => 'Music Session History' do +ActiveAdmin.register JamRuby::MusicSession, :as => 'Music Session' do config.filters = false config.per_page = 50 @@ -9,14 +9,14 @@ ActiveAdmin.register JamRuby::MusicSessionHistory, :as => 'Music Session History controller do def scoped_collection if params['admin'].blank? || '1' == params['admin'] - @music_session_histories ||= end_of_association_chain - .includes([:user, :band]) + @music_sessions ||= end_of_association_chain + .includes([:creator, :band]) .order('created_at DESC') else - @music_session_histories ||= end_of_association_chain - .joins('INNER JOIN users AS uu ON uu.id = music_sessions_history.user_id') + @music_sessions ||= end_of_association_chain + .joins('INNER JOIN users AS uu ON uu.id = music_sessions.user_id') .where(['uu.admin = ?','f']) - .includes([:user, :band]) + .includes([:creator, :band]) .order('created_at DESC') end end @@ -33,9 +33,9 @@ ActiveAdmin.register JamRuby::MusicSessionHistory, :as => 'Music Session History row :description row :duration do |msh| "#{msh.duration_minutes} minutes" end row :active do |msh| msh.session_removed_at.nil? end - row :creator do |msh| auto_link(msh.user, msh.user.try(:email)) end + row :creator do |msh| auto_link(msh.creator, msh.creator.try(:email)) end row :band do |msh| auto_link(msh.band, msh.band.try(:name)) end - row :genres + row :genre end end end diff --git a/admin/app/assets/stylesheets/custom.css.scss b/admin/app/assets/stylesheets/custom.css.scss index d3dabb8d0..f253c6e8d 100644 --- a/admin/app/assets/stylesheets/custom.css.scss +++ b/admin/app/assets/stylesheets/custom.css.scss @@ -4,8 +4,13 @@ color:lightgray; } +.text-center { + text-align: center; +} + .feed-pagination { height: 20px; + margin-bottom: 15px; .pagination { float: left !important; diff --git a/admin/app/views/admin/feed/_form.html.erb b/admin/app/views/admin/feed/_form.html.erb index b72d211e3..87ff1936b 100644 --- a/admin/app/views/admin/feed/_form.html.erb +++ b/admin/app/views/admin/feed/_form.html.erb @@ -1,6 +1,6 @@ <%= semantic_form_for :feed, url: admin_feed_path, method: :get do |f| %> <%= f.inputs do %> - <%= f.input :user, :as => :autocomplete, :url => autocomplete_user_email_admin_users_path, :input_html => { :id_element => "#feed_user_id" }%> + <%= f.input :user, :as => :autocomplete, :url => autocomplete_user_email_admin_users_path, :input_html => { :id_element => "#feed_user_id" } %> <%= f.input :user_id, :as => :hidden %> <% end %> <% end %> \ No newline at end of file diff --git a/admin/app/views/admin/feed/_index.html.erb b/admin/app/views/admin/feed/_index.html.erb deleted file mode 100644 index 7ad3e71d2..000000000 --- a/admin/app/views/admin/feed/_index.html.erb +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - <% @users.each do |user| %> - - - - <% end %> - -
User Name - <%= @user_id %>
-

- <%= link_to admin_feed_path(feed: {user_id: user.id}) do %> - <%= user.name %> - <% end %>

-
- -
- <%= will_paginate @users %> -
\ No newline at end of file diff --git a/admin/log/phantomjs.out b/admin/log/phantomjs.out new file mode 100644 index 000000000..e69de29bb diff --git a/admin/spec/factories.rb b/admin/spec/factories.rb index 227ce212f..bc3ac8ecf 100644 --- a/admin/spec/factories.rb +++ b/admin/spec/factories.rb @@ -23,7 +23,7 @@ FactoryGirl.define do factory :single_user_session do after(:create) do |user, evaluator| - music_session = FactoryGirl.create(:music_session, :creator => user) + music_session = FactoryGirl.create(:active_music_session, :creator => user) connection = FactoryGirl.create(:connection, :user => user, :music_session => music_session) end end @@ -46,4 +46,131 @@ FactoryGirl.define do description { |n| "Instrument #{n}" } end + factory :genre, :class => JamRuby::Genre do + description { |n| "Genre #{n}" } + end + + factory :music_session, :class => JamRuby::MusicSession do + sequence(:name) { |n| "Music Session #{n}" } + sequence(:description) { |n| "Music Session Description #{n}" } + fan_chat true + fan_access true + approval_required false + musician_access true + legal_terms true + language 'english' + legal_policy 'standard' + genre JamRuby::Genre.first + association :creator, :factory => :user + end + + factory :music_session_user_history, :class => JamRuby::MusicSessionUserHistory do + ignore do + history nil + user nil + end + + music_session_id { history.id } + user_id { user.id } + sequence(:client_id) { |n| "Connection #{n}" } + end + + factory :recorded_track, :class => JamRuby::RecordedTrack do + instrument JamRuby::Instrument.first + sound 'stereo' + sequence(:client_id) { |n| "client_id-#{n}"} + sequence(:track_id) { |n| "track_id-#{n}"} + sequence(:client_track_id) { |n| "client_track_id-#{n}"} + md5 'abc' + length 1 + fully_uploaded true + association :user, factory: :user + association :recording, factory: :recording + end + + factory :recording, :class => JamRuby::Recording do + + association :owner, factory: :user + association :music_session, factory: :active_music_session + + factory :recording_with_track do + before(:create) { |recording| + recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: recording.owner) + } + end + end + + factory :claimed_recording, :class => JamRuby::ClaimedRecording do + sequence(:name) { |n| "name-#{n}" } + sequence(:description) { |n| "description-#{n}" } + is_public true + association :genre, factory: :genre + association :user, factory: :user + + before(:create) { |claimed_recording| + claimed_recording.recording = FactoryGirl.create(:recording_with_track, owner: claimed_recording.user) + } + + end + + factory :mix, :class => JamRuby::Mix do + started_at Time.now + completed_at Time.now + ogg_md5 'abc' + ogg_length 1 + sequence(:ogg_url) { |n| "recordings/ogg/#{n}" } + mp3_md5 'abc' + mp3_length 1 + sequence(:mp3_url) { |n| "recordings/mp3/#{n}" } + completed true + + before(:create) {|mix| + user = FactoryGirl.create(:user) + mix.recording = FactoryGirl.create(:recording_with_track, owner: user) + mix.recording.claimed_recordings << FactoryGirl.create(:claimed_recording, user: user, recording: mix.recording) + } + end + + factory :diagnostic, :class => JamRuby::Diagnostic do + type JamRuby::Diagnostic::NO_HEARTBEAT_ACK + creator JamRuby::Diagnostic::CLIENT + data Faker::Lorem.sentence + association :user, factory: :user + end + + factory :active_music_session_no_user_history, :class => JamRuby::ActiveMusicSession do + + association :creator, factory: :user + + ignore do + name "My Music Session" + description "Come Music Session" + fan_chat true + fan_access true + approval_required false + musician_access true + legal_terms true + genre JamRuby::Genre.first + band nil + end + + + before(:create) do |session, evaluator| + music_session = FactoryGirl.create(:music_session, name: evaluator.name, description: evaluator.description, fan_chat: evaluator.fan_chat, + fan_access: evaluator.fan_access, approval_required: evaluator.approval_required, musician_access: evaluator.musician_access, + genre: evaluator.genre, creator: evaluator.creator, band: evaluator.band) + session.id = music_session.id + end + + factory :active_music_session do + after(:create) { |session| + FactoryGirl.create(:music_session_user_history, :history => session.music_session, :user => session.creator) + } + + factory :music_session_with_mount do + association :mount, :factory => :icecast_mount + end + end + end + end diff --git a/admin/spec/features/feeds_spec.rb b/admin/spec/features/feeds_spec.rb new file mode 100644 index 000000000..7c468c2f1 --- /dev/null +++ b/admin/spec/features/feeds_spec.rb @@ -0,0 +1,81 @@ +require 'spec_helper' + +describe 'Feeds' do + + subject { page } + + before do + MusicSession.delete_all + Recording.delete_all + Diagnostic.delete_all + User.delete_all + end + + let(:admin) { FactoryGirl.create(:admin) } + let(:user) { FactoryGirl.create(:user) } + + let(:music_session) { FactoryGirl.create(:music_session, :creator => user) } + let(:recording) { FactoryGirl.create(:recording, :owner => user) } + let(:diagnostic) { FactoryGirl.create(:diagnostic, :user => user) } + + describe 'empty dashboard' do + before do + visit admin_feed_path + end + + it { should have_selector('h2', text: 'Feed') } + + it 'has no feeds' do + should have_selector('p', text: 'No session activities.') + should have_selector('p', text: 'No recording activities.') + should have_selector('p', text: 'No diagnostic activities.') + end + + describe 'admin enters user name' do + it 'auto-completes with email + full name', :js => true do + within('form.feed') do + fill_in 'feed_user', with: user.email[0..3] + end + + page.execute_script %Q{ $('form.feed input#feed_user').trigger('focus') } + page.execute_script %Q{ $('form.feed input#feed_user').trigger('keydown') } + sleep 5 + find('a.ui-corner-all', text: user.to_label).trigger(:click) + should have_selector('form.feed #feed_user', user.to_label) + should have_selector('form.feed #feed_user_id', user.id) + end + end + end + + describe 'activities' do + before do + + visit admin_feed_path + end + + it 'shows session, recording, diagnostic' do + should have_selector("tr#jam_ruby_music_session_#{music_session.id}") + should have_selector("tr#jam_ruby_recording_#{recording.id}") + should have_selector("tr#jam_ruby_diagnostic_#{diagnostic.id}") + end + + it 'shows activities for one user', :js => true do + within('form.feed') do + fill_in 'feed_user', with: user.email[0..3] + end + + page.execute_script %Q{ $('form.feed input#feed_user').trigger('focus') } + page.execute_script %Q{ $('form.feed input#feed_user').trigger('keydown') } + sleep 5 + find('a.ui-corner-all', text: user.to_label).trigger(:click) + should have_selector('form.feed #feed_user', user.to_label) + should have_selector('form.feed #feed_user_id', user.id) + + find('form.feed').trigger(:submit) + + should have_selector("tr#jam_ruby_music_session_#{music_session.id}") + should have_selector("tr#jam_ruby_recording_#{recording.id}") + should have_selector("tr#jam_ruby_diagnostic_#{diagnostic.id}") + end + end +end \ No newline at end of file diff --git a/admin/spec/spec_helper.rb b/admin/spec/spec_helper.rb index f7ddad41a..6f8ef3d04 100644 --- a/admin/spec/spec_helper.rb +++ b/admin/spec/spec_helper.rb @@ -22,6 +22,9 @@ require 'rspec/autorun' # load capybara require 'capybara/rails' +require 'capybara/rspec' +require 'capybara-screenshot/rspec' +require 'capybara/poltergeist' #include Rails.application.routes.url_helpers @@ -30,6 +33,11 @@ require 'capybara/rails' Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} +Capybara.register_driver :poltergeist do |app| + driver = Capybara::Poltergeist::Driver.new(app, { debug: false, phantomjs_logger: File.open('log/phantomjs.out', 'w') }) +end +Capybara.javascript_driver = :poltergeist +Capybara.default_wait_time = 10 RSpec.configure do |config| # ## Mock Framework diff --git a/admin/spec/support/snapshot.rb b/admin/spec/support/snapshot.rb new file mode 100644 index 000000000..e77123edf --- /dev/null +++ b/admin/spec/support/snapshot.rb @@ -0,0 +1,30 @@ +module Snapshot + + SS_PATH = 'snapshots.html' + + def set_up_snapshot(filepath = SS_PATH) + @size = [1280, 720] #arbitrary + @file = File.new(filepath, "w+") + @file.puts "" + @file.puts "

Snapshot #{ENV["BUILD_NUMBER"]} - #{@size[0]}x#{@size[1]}

" + end + + def snapshot_example + page.driver.resize(@size[0], @size[1]) + @file.puts "

Example name: #{get_description}



" + end + + def snap!(title = get_description) + base64 = page.driver.render_base64(:png) + @file.puts '

' + title + '

' + @file.puts '' + title +'' + @file.puts '


' + end + + def tear_down_snapshot + @file.puts "" + @file.close() + end + +end + diff --git a/db/manifest b/db/manifest index 637e56cc4..d13ae148c 100755 --- a/db/manifest +++ b/db/manifest @@ -151,3 +151,5 @@ user_mods.sql connection_stale_expire.sql rename_chat_messages.sql fix_connection_fields.sql +session_ratings.sql +scheduled_sessions.sql \ No newline at end of file diff --git a/db/up/scheduled_sessions.sql b/db/up/scheduled_sessions.sql new file mode 100644 index 000000000..2c2c09777 --- /dev/null +++ b/db/up/scheduled_sessions.sql @@ -0,0 +1,151 @@ +-- track the last measured audio gear latency +ALTER TABLE users ADD COLUMN audio_latency double precision; + +-- begin moving all the fields traditionally on music_sessions to music_sessions_history +ALTER TABLE music_sessions_history ADD COLUMN scheduled_start TIMESTAMP WITH TIME ZONE; +ALTER TABLE music_sessions_history ADD COLUMN scheduled_duration INTERVAL; +ALTER TABLE music_sessions_history ADD COLUMN musician_access BOOLEAN NOT NULL DEFAULT TRUE; +ALTER TABLE music_sessions_history ADD COLUMN approval_required BOOLEAN NOT NULL DEFAULT FALSE; +ALTER TABLE music_sessions_history ADD COLUMN fan_chat BOOLEAN NOT NULL DEFAULT TRUE; +ALTER TABLE music_sessions_history ADD COLUMN genre_id VARCHAR(64) REFERENCES genres(id); +ALTER TABLE music_sessions_history ADD COLUMN legal_policy VARCHAR(255) NOT NULL DEFAULT 'standard'; +ALTER TABLE music_sessions_history ADD COLUMN language VARCHAR(255) NOT NULL DEFAULT 'en'; +ALTER TABLE music_sessions_history ADD COLUMN name TEXT; + +-- get rid of genres in favor of just genre_id (no more multi-genres for a session) +UPDATE music_sessions_history SET name = description; +ALTER TABLE music_sessions_history ALTER COLUMN name SET NOT NULL; +-- production db has some null genres on older sessions +UPDATE music_sessions_history SET genres = 'rock' where genres = ''; +UPDATE music_sessions_history SET genre_id = genres; +ALTER TABLE music_sessions_history ALTER COLUMN genre_id SET NOT NULL; +ALTER TABLE music_sessions_history DROP COLUMN genres; + +-- likers should refer to id field of music_sessions_history (not music_session_id) +ALTER TABLE music_sessions_likers ADD COLUMN music_session_id2 VARCHAR(64) REFERENCES music_sessions_history(id) ON DELETE CASCADE; +-- production db has some bad data +DELETE from music_sessions_likers where music_session_id NOT IN (select id from music_sessions_history); +UPDATE music_sessions_likers SET music_session_id2 = music_session_id; +ALTER TABLE music_sessions_likers DROP COLUMN music_session_id; +ALTER TABLE music_sessions_likers RENAME COLUMN music_session_id2 to music_session_id; + +-- comments should refer to id field of music_sessions_history (not music_session_id) +ALTER TABLE music_sessions_comments ADD COLUMN music_session_id2 VARCHAR(64) REFERENCES music_sessions_history(id) ON DELETE CASCADE; +-- production db has some bad data +DELETE from music_sessions_comments where music_session_id NOT IN (select id from music_sessions_history); +UPDATE music_sessions_comments SET music_session_id2 = music_session_id; +ALTER TABLE music_sessions_comments DROP COLUMN music_session_id; +ALTER TABLE music_sessions_comments RENAME COLUMN music_session_id2 to music_session_id; + +-- user_history should refer to id field of music_sessions_history (not music_session_id) +ALTER TABLE music_sessions_user_history ADD COLUMN music_session_id2 VARCHAR(64) REFERENCES music_sessions_history(id) ON DELETE CASCADE; +-- production db has some bad data +DELETE from music_sessions_user_history where music_session_id NOT IN (select id from music_sessions_history); +UPDATE music_sessions_user_history SET music_session_id2 = music_session_id; +ALTER TABLE music_sessions_user_history DROP COLUMN music_session_id; +ALTER TABLE music_sessions_user_history RENAME COLUMN music_session_id2 to music_session_id; + +-- get rid of display fields on music_sessions +ALTER TABLE music_sessions DROP COLUMN musician_access; +ALTER TABLE music_sessions DROP COLUMN fan_access; +ALTER TABLE music_sessions DROP COLUMN description; +ALTER TABLE music_sessions DROP COLUMN fan_chat; +ALTER TABLE music_sessions DROP COLUMN approval_required; +ALTER TABLE music_sessions DROP COLUMN band_id; + +ALTER TABLE music_sessions_history ALTER COLUMN music_session_id DROP NOT NULL; + +-- create RSVP slots +CREATE TABLE rsvp_slots ( + id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL, + instrument_id VARCHAR(64) REFERENCES instruments (id), + proficiency_level VARCHAR(255) NOT NULL, + music_session_id VARCHAR(64) NOT NULL REFERENCES music_sessions_history (id) ON DELETE CASCADE, + created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL, + updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL +); + +-- create RSVP requests +CREATE TABLE rsvp_requests ( + id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL, + user_id VARCHAR(64) NOT NULL REFERENCES users (id) ON DELETE CASCADE, + rsvp_slot_id VARCHAR(64) NOT NULL REFERENCES rsvp_slots(id) ON DELETE CASCADE, + message TEXT, + chosen BOOLEAN DEFAULT FALSE, + canceled BOOLEAN DEFAULT FALSE, + created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL, + updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL +); + +CREATE TABLE recurring_sessions ( + id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL, + description VARCHAR(8000), + scheduled_start TIMESTAMP WITH TIME ZONE, + scheduled_duration INTERVAL, + musician_access BOOLEAN NOT NULL, + approval_required BOOLEAN NOT NULL, + fan_chat BOOLEAN NOT NULL, + genre_id VARCHAR(64) REFERENCES genres(id), + legal_policy VARCHAR(255) NOT NULL DEFAULT 'standard', + language VARCHAR(255) NOT NULL DEFAULT 'en', + name TEXT, + user_id VARCHAR(64) NOT NULL REFERENCES users (id) ON DELETE CASCADE, + band_id VARCHAR(64) REFERENCES bands(id) ON DELETE CASCADE, + created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL, + updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL +); + +ALTER TABLE music_sessions_history ADD COLUMN recurring_session_id VARCHAR(64) REFERENCES recurring_sessions(id); + +-- make these 3 tables be LOGGED, and refer to music_sessions_history instead of music_sessions +DROP TABLE fan_invitations; +DROP TABLE invitations; +DROP TABLE join_requests; + + +CREATE TABLE fan_invitations ( + id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL, + sender_id VARCHAR(64), + receiver_id VARCHAR(64), + music_session_id VARCHAR(64), + created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL, + updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL +); +ALTER TABLE ONLY fan_invitations ADD CONSTRAINT fan_invitations_music_session_id_fkey FOREIGN KEY (music_session_id) REFERENCES music_sessions_history(id) ON DELETE CASCADE; + +CREATE UNLOGGED TABLE join_requests ( + id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL, + user_id VARCHAR(64), + music_session_id VARCHAR(64), + text VARCHAR(2000), + created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL, + updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL +); +ALTER TABLE ONLY join_requests ADD CONSTRAINT user_music_session_uniqkey UNIQUE (user_id, music_session_id); +ALTER TABLE ONLY join_requests ADD CONSTRAINT join_requests_music_session_id_fkey FOREIGN KEY (music_session_id) REFERENCES music_sessions_history(id) ON DELETE CASCADE; + +-- INVITATIONS +-------------- +CREATE UNLOGGED TABLE invitations ( + id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL, + sender_id VARCHAR(64), + receiver_id VARCHAR(64), + music_session_id VARCHAR(64), + created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL, + updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL, + join_request_id VARCHAR(64) +); +ALTER TABLE ONLY invitations ADD CONSTRAINT invitations_uniqkey UNIQUE (sender_id, receiver_id, music_session_id); +ALTER TABLE ONLY invitations ADD CONSTRAINT invitations_join_request_id_fkey FOREIGN KEY (join_request_id) REFERENCES join_requests(id) ON DELETE CASCADE; +ALTER TABLE ONLY invitations ADD CONSTRAINT invitations_music_session_id_fkey FOREIGN KEY (music_session_id) REFERENCES music_sessions_history(id) ON DELETE CASCADE; + + +-- finally, rename music_sessions and music_sessions_history to reflect true nature better +ALTER TABLE music_sessions RENAME TO active_music_sessions; +ALTER TABLE music_sessions_history RENAME TO music_sessions; + +-- add fk to chat_messages so they delete cleanly when users are deleted +ALTER TABLE ONLY chat_messages ADD CONSTRAINT chat_messages_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; + +-- fix any promotionals +UPDATE promotionals SET latest_type = 'JamRuby::MusicSession' WHERE latest_type = 'JamRuby::MusicSessionHistory'; \ No newline at end of file diff --git a/db/up/session_ratings.sql b/db/up/session_ratings.sql new file mode 100644 index 000000000..68223a849 --- /dev/null +++ b/db/up/session_ratings.sql @@ -0,0 +1 @@ +ALTER TABLE music_sessions_user_history ADD COLUMN rating_comment TEXT; diff --git a/pb/src/client_container.proto b/pb/src/client_container.proto index 51ec3308d..276349154 100644 --- a/pb/src/client_container.proto +++ b/pb/src/client_container.proto @@ -186,6 +186,7 @@ message LoginAck { optional string music_session_id = 5; // the music session that the user was in very recently (likely due to dropped connection) optional bool reconnected = 6; // if reconnect_music_session_id is specified, and the server could log the user into that session, then true is returned. optional string user_id = 7; // the database user id + optional int32 connection_expire_time = 8; // this is how long the server gives you before killing your connection entirely after missing heartbeats } // route_to: server diff --git a/ruby/Gemfile b/ruby/Gemfile index 252c1f546..7a53e7f6b 100644 --- a/ruby/Gemfile +++ b/ruby/Gemfile @@ -4,7 +4,7 @@ unless ENV["LOCAL_DEV"] == "1" source 'https://jamjam:blueberryjam@int.jamkazam.com/gems/' end -devenv = ENV["BUILD_NUMBER"].nil? # Jenkins sets a build number environment variable +devenv = ENV["BUILD_NUMBER"].nil? || ENV["TEST_WWW"] == "1" if devenv gem 'jam_db', :path=> "../db/target/ruby_package" diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb index c0eeeb87d..2ea2b5a2b 100755 --- a/ruby/lib/jam_ruby.rb +++ b/ruby/lib/jam_ruby.rb @@ -31,6 +31,7 @@ require "jam_ruby/lib/module_overrides" require "jam_ruby/lib/s3_util" require "jam_ruby/lib/s3_manager" require "jam_ruby/lib/profanity" +require "jam_ruby/lib/json_validator" require "jam_ruby/lib/em_helper.rb" require "jam_ruby/lib/nav.rb" require "jam_ruby/resque/audiomixer" @@ -75,10 +76,11 @@ require "jam_ruby/models/artifact_update" require "jam_ruby/models/band_invitation" require "jam_ruby/models/band_musician" require "jam_ruby/models/connection" +require "jam_ruby/models/diagnostic" require "jam_ruby/models/friendship" -require "jam_ruby/models/music_session" +require "jam_ruby/models/active_music_session" require "jam_ruby/models/music_session_comment" -require "jam_ruby/models/music_session_history" +require "jam_ruby/models/music_session" require "jam_ruby/models/music_session_liker" require "jam_ruby/models/music_session_user_history" require "jam_ruby/models/music_session_perf_data" diff --git a/ruby/lib/jam_ruby/app/mailers/batch_mailer.rb b/ruby/lib/jam_ruby/app/mailers/batch_mailer.rb index 7b200eb3b..17f238090 100644 --- a/ruby/lib/jam_ruby/app/mailers/batch_mailer.rb +++ b/ruby/lib/jam_ruby/app/mailers/batch_mailer.rb @@ -15,7 +15,7 @@ module JamRuby batch.did_send(emails) - mail(:to => emails, + mail(:to => emails, :from => batch.from_email, :subject => batch.subject) do |format| format.text diff --git a/ruby/lib/jam_ruby/connection_manager.rb b/ruby/lib/jam_ruby/connection_manager.rb index 59e7b8735..589841943 100644 --- a/ruby/lib/jam_ruby/connection_manager.rb +++ b/ruby/lib/jam_ruby/connection_manager.rb @@ -39,12 +39,12 @@ module JamRuby # this simulates music_session destroy callbacks with activerecord def before_destroy_music_session(music_session_id) - music_session = MusicSession.find_by_id(music_session_id) + music_session = ActiveMusicSession.find_by_id(music_session_id) music_session.before_destroy if music_session end # reclaim the existing connection, if ip_address is not nil then perhaps a new address as well - def reconnect(conn, reconnect_music_session_id, ip_address) + def reconnect(conn, reconnect_music_session_id, ip_address, connection_stale_time, connection_expire_time) music_session_id = nil reconnected = false @@ -86,7 +86,7 @@ module JamRuby end sql =< public_ip, :client_id => client_id, @@ -61,7 +61,8 @@ module JamRuby :heartbeat_interval => heartbeat_interval, :music_session_id => music_session_id, :reconnected => reconnected, - :user_id => user_id + :user_id => user_id, + :connection_expire_time => connection_expire_time ) Jampb::ClientMessage.new( diff --git a/ruby/lib/jam_ruby/models/active_music_session.rb b/ruby/lib/jam_ruby/models/active_music_session.rb new file mode 100644 index 000000000..248879bbc --- /dev/null +++ b/ruby/lib/jam_ruby/models/active_music_session.rb @@ -0,0 +1,482 @@ +module JamRuby + class ActiveMusicSession < ActiveRecord::Base + self.primary_key = 'id' + + self.table_name = 'active_music_sessions' + + attr_accessor :legal_terms, :max_score + + belongs_to :claimed_recording, :class_name => "JamRuby::ClaimedRecording", :foreign_key => "claimed_recording_id", :inverse_of => :playing_sessions + belongs_to :claimed_recording_initiator, :class_name => "JamRuby::User", :inverse_of => :playing_claimed_recordings, :foreign_key => "claimed_recording_initiator_id" + + has_one :music_session, :class_name => "JamRuby::MusicSession", :foreign_key => 'music_session_id' + has_one :mount, :class_name => "JamRuby::IcecastMount", :inverse_of => :music_session, :foreign_key => 'music_session_id' + belongs_to :creator, :class_name => 'JamRuby::User', :foreign_key => :user_id + + has_many :connections, :class_name => "JamRuby::Connection", foreign_key: :music_session_id + has_many :users, :through => :connections, :class_name => "JamRuby::User" + has_many :recordings, :class_name => "JamRuby::Recording", :inverse_of => :music_session, foreign_key: :music_session_id + has_many :chats, :class_name => "JamRuby::ChatMessages", :foreign_key => "session_id" + validates :creator, :presence => true + validate :creator_is_musician + validate :no_new_playback_while_playing + + after_create :started_session + + after_destroy do |obj| + JamRuby::MusicSession.removed_music_session(obj.id) + end + + #default_scope :select => "*, 0 as score" + + def attributes + super.merge('max_score' => self.max_score) + end + + def max_score + nil unless has_attribute?(:max_score) + read_attribute(:max_score).to_i + end + + before_create :create_uuid + def create_uuid + #self.id = SecureRandom.uuid + end + + def before_destroy + self.mount.destroy if self.mount + end + + def creator_is_musician + unless creator.musician? + errors.add(:creator, ValidationMessages::MUST_BE_A_MUSICIAN) + end + end + + def no_new_playback_while_playing + # if we previous had a claimed recording and are trying to set one + # and if also the previous initiator is different than the current one... it's a no go + if !claimed_recording_id_was.nil? && !claimed_recording_id.nil? && + claimed_recording_initiator_id_was != claimed_recording_initiator_id + errors.add(:claimed_recording, ValidationMessages::CLAIMED_RECORDING_ALREADY_IN_PROGRESS) + end + end + + # returns an array of client_id's that are in this session + # if as_musician is nil, all connections in the session ,regardless if it's a musician or not or not + # you can also exclude a client_id from the returned set by setting exclude_client_id + def get_connection_ids(options = {}) + as_musician = options[:as_musician] + exclude_client_id = options[:exclude_client_id] + + where = { :music_session_id => self.id } + where[:as_musician] = as_musician unless as_musician.nil? + + exclude = "client_id != '#{exclude_client_id}'"unless exclude_client_id.nil? + + Connection.select(:client_id).where(where).where(exclude).map(&:client_id) + end + + # This is a little confusing. You can specify *BOTH* friends_only and my_bands_only to be true + # If so, then it's an OR condition. If both are false, you can get sessions with anyone. + def self.index(current_user, options = {}) + participants = options[:participants] + genres = options[:genres] + keyword = options[:keyword] + friends_only = options[:friends_only].nil? ? false : options[:friends_only] + my_bands_only = options[:my_bands_only].nil? ? false : options[:my_bands_only] + as_musician = options[:as_musician].nil? ? true : options[:as_musician] + + query = ActiveMusicSession + .joins( + %Q{ + INNER JOIN + music_sessions + ON + active_music_sessions.id = music_sessions.id + } + ) + .joins( + %Q{ + INNER JOIN + connections + ON + active_music_sessions.id = connections.music_session_id + } + ) + .joins( + %Q{ + LEFT OUTER JOIN + friendships + ON + connections.user_id = friendships.user_id + AND + friendships.friend_id = '#{current_user.id}' + } + ) + .joins( + %Q{ + LEFT OUTER JOIN + invitations + ON + invitations.music_session_id = active_music_sessions.id + AND + invitations.receiver_id = '#{current_user.id}' + } + ) + .group( + %Q{ + active_music_sessions.id + } + ) + .order( + %Q{ + SUM(CASE WHEN invitations.id IS NULL THEN 0 ELSE 1 END) DESC, + SUM(CASE WHEN friendships.user_id IS NULL THEN 0 ELSE 1 END) DESC, + active_music_sessions.created_at DESC + } + ) + + if as_musician + query = query.where( + %Q{ + musician_access = true + OR + invitations.id IS NOT NULL + } + ) + else + # if you are trying to join the session as a fan/listener, + # we have to have a mount, fan_access has to be true, and we have to allow for the reload of icecast to have taken effect + query = query.joins('INNER JOIN icecast_mounts ON icecast_mounts.music_session_id = active_music_sessions.id INNER JOIN icecast_servers ON icecast_mounts.icecast_server_id = icecast_servers.id') + query = query.where('music_sessions.fan_access = true') + query = query.where("(active_music_sessions.created_at < icecast_servers.config_updated_at)") + end + + query = query.where("music_sessions.description like '%#{keyword}%'") unless keyword.nil? + query = query.where("connections.user_id" => participants.split(',')) unless participants.nil? + query = query.where("music_sessions.genre_id in (?)", genres) unless genres.nil? + + if my_bands_only + query = query.joins( + %Q{ + LEFT OUTER JOIN + bands_musicians + ON + bands_musicians.user_id = '#{current_user.id}' + } + ) + end + + if my_bands_only || friends_only + query = query.where( + %Q{ + #{friends_only ? "friendships.user_id IS NOT NULL" : "false"} + OR + #{my_bands_only ? "bands_musicians.band_id = music_sessions.band_id" : "false"} + } + ) + end + + return query + end + + # This is a little confusing. You can specify *BOTH* friends_only and my_bands_only to be true + # If so, then it's an OR condition. If both are false, you can get sessions with anyone. + # note, this is mostly the same as above but includes paging through the result and and scores. + # thus it needs the client_id... + def self.nindex(current_user, options = {}) + client_id = options[:client_id] + participants = options[:participants] + genres = options[:genres] + keyword = options[:keyword] + friends_only = options[:friends_only].nil? ? false : options[:friends_only] + my_bands_only = options[:my_bands_only].nil? ? false : options[:my_bands_only] + as_musician = options[:as_musician].nil? ? true : options[:as_musician] + offset = options[:offset] + limit = options[:limit] + + connection = Connection.where(client_id: client_id).first! + locidispid = connection.locidispid + + query = ActiveMusicSession + .select("active_music_sessions.*, max(coalesce(current_scores.score, 1000)) as max_score") # 1000 is higher than the allowed max of 999 + .joins( + %Q{ + INNER JOIN + music_sessions + ON + active_music_sessions.id = music_sessions.id + } + ) + .joins( + %Q{ + INNER JOIN + connections + ON + active_music_sessions.id = connections.music_session_id + } + ) + .joins( + %Q{ + LEFT OUTER JOIN + current_scores + ON + current_scores.alocidispid = connections.locidispid + AND + current_scores.blocidispid = #{locidispid} + } + ) + .joins( + %Q{ + LEFT OUTER JOIN + friendships + ON + connections.user_id = friendships.user_id + AND + friendships.friend_id = '#{current_user.id}' + } + ) + .joins( + %Q{ + LEFT OUTER JOIN + invitations + ON + invitations.music_session_id = active_music_sessions.id + AND + invitations.receiver_id = '#{current_user.id}' + } + ) + .group( + %Q{ + active_music_sessions.id + } + ) + .order( + %Q{ + SUM(CASE WHEN invitations.id IS NULL THEN 0 ELSE 1 END) DESC, + SUM(CASE WHEN friendships.user_id IS NULL THEN 0 ELSE 1 END) DESC, + active_music_sessions.created_at DESC + } + ) + + if (offset) + query = query.offset(offset) + end + + if (limit) + query = query.limit(limit) + end + + if as_musician + query = query.where( + %Q{ + musician_access = true + OR + music_sessions.user_id = '#{current_user.id}' + OR + invitations.id IS NOT NULL + } + ) + else + # if you are trying to join the session as a fan/listener, + # we have to have a mount, fan_access has to be true, and we have to allow for the reload of icecast to have taken effect + query = query.joins('INNER JOIN icecast_mounts ON icecast_mounts.music_session_id = active_music_sessions.id INNER JOIN icecast_servers ON icecast_mounts.icecast_server_id = icecast_servers.id') + query = query.where('music_sessions.fan_access = true') + query = query.where("(active_music_sessions.created_at < icecast_servers.config_updated_at)") + end + + query = query.where("music_sessions.description like '%#{keyword}%'") unless keyword.nil? + query = query.where("connections.user_id" => participants.split(',')) unless participants.nil? + query = query.where("music_sessions.genre_id in (?)", genres) unless genres.nil? + + if my_bands_only + query = query.joins( + %Q{ + LEFT OUTER JOIN + bands_musicians + ON + bands_musicians.user_id = '#{current_user.id}' + } + ) + end + + if my_bands_only || friends_only + query = query.where( + %Q{ + #{friends_only ? "friendships.user_id IS NOT NULL" : "false"} + OR + #{my_bands_only ? "bands_musicians.band_id = music_sessions.band_id" : "false"} + } + ) + end + + return query + end + + # Verifies that the specified user can join this music session + def can_join? user, as_musician + if as_musician + if !user.musician + return false # "a fan can not join a music session as a musician" + raise PermissionError, "a fan can not join a music session as a musician" + end + + if self.musician_access + if self.approval_required + return self.invited_musicians.exists?(user) + else + return true + end + + else + # the creator can always join, and the invited users can join + return self.creator == user || self.invited_musicians.exists?(user) + end + else + # it's a fan, and the only way a fan can join is if fan_access is true + return self.fan_access + end + + end + + # Verifies that the specified user can see this music session + def can_see? user + if self.musician_access || self.fan_access + true + else + self.creator == user || self.invited_musicians.exists?(user) + end + end + + # Verifies that the specified user can delete this music session + def can_delete? user + # the creator can delete + self.creator == user + end + + def access? user + music_session.part_of_session? user + end + + def most_recent_recording + recordings.where(:music_session_id => self.id).order('created_at desc').limit(1).first + end + + # is this music session currently recording? + def is_recording? + recordings.where(:duration => nil).count > 0 + end + + def is_playing_recording? + !self.claimed_recording.nil? + end + + def recording + recordings.where(:duration => nil).first + end + + # stops any active recording + def stop_recording + current_recording = self.recording + current_recording.stop unless current_recording.nil? + end + + def claimed_recording_start(owner, claimed_recording) + self.claimed_recording = claimed_recording + self.claimed_recording_initiator = owner + self.save + end + + def claimed_recording_stop + self.claimed_recording = nil + self.claimed_recording_initiator = nil + self.save + end + + def invitations + music_session.invitations + end + + def invited_musicians + music_session.invited_musicians + end + + def join_requests + music_session.join_requests + end + + def fan_invitations + music_session.fan_invitations + end + + def to_s + description + end + + def musician_access + music_session.musician_access + end + + def fan_access + music_session.fan_access + end + + def description + music_session.description + end + + def genre + music_session.genre + end + + def fan_chat + music_session.fan_chat + end + + def band + music_session.band + end + + def approval_required + music_session.approval_required + end + + def tick_track_changes + self.track_changes_counter += 1 + self.save!(:validate => false) + end + + def connected_participant_count + Connection.where(:music_session_id => self.id, + :aasm_state => Connection::CONNECT_STATE.to_s, + :as_musician => true) + .count + end + + def started_session + raise "active_music_sessions.id must be set by caller" unless self.id + # associate this active_music_session with the music_session formally + session = MusicSession.find(self.id) + session.active_music_session = self + session.save! + + GoogleAnalyticsEvent.track_session_duration(self) + GoogleAnalyticsEvent.track_band_real_session(self) + end + + + def self.sync(session_history) + music_session = MusicSession.find_by_id(session_history.id) + + if music_session.nil? + music_session = MusicSession.new + music_session.id = session_history.id + end + + music_session.user_id = session_history.creator.id + music_session.band_id = session_history.band.id unless session_history.band.nil? + session_history.save! + end + end +end diff --git a/ruby/lib/jam_ruby/models/band.rb b/ruby/lib/jam_ruby/models/band.rb index 3a4a07f60..f1e68e055 100644 --- a/ruby/lib/jam_ruby/models/band.rb +++ b/ruby/lib/jam_ruby/models/band.rb @@ -44,8 +44,7 @@ module JamRuby has_many :invitations, :inverse_of => :band, :class_name => "JamRuby::BandInvitation", :foreign_key => "band_id" # music_sessions - has_many :music_sessions, :class_name => "JamRuby::MusicSession", :foreign_key => "band_id" - has_many :music_session_history, :class_name => "JamRuby::MusicSessionHistory", :foreign_key => "band_id", :inverse_of => :band + has_many :music_sessions, :class_name => "JamRuby::MusicSession", foreign_key: :band_id, :inverse_of => :band # events has_many :event_sessions, :class_name => "JamRuby::EventSession" @@ -54,19 +53,19 @@ module JamRuby acts_as_mappable def liker_count - return self.likers.size + self.likers.size end def follower_count - return self.followers.size + self.followers.size end def recording_count - return self.recordings.size + self.recordings.size end def session_count - return self.music_sessions.size + self.music_sessions.size end def recent_history @@ -74,7 +73,7 @@ module JamRuby .order('created_at DESC') .limit(10) - msh = MusicSessionHistory.where(:band_id => self.id) + msh = MusicSession.where(:band_id => self.id) .order('created_at DESC') .limit(10) diff --git a/ruby/lib/jam_ruby/models/chat_message.rb b/ruby/lib/jam_ruby/models/chat_message.rb index b23aa711e..c02ed1d7b 100644 --- a/ruby/lib/jam_ruby/models/chat_message.rb +++ b/ruby/lib/jam_ruby/models/chat_message.rb @@ -11,6 +11,7 @@ module JamRuby belongs_to :user belongs_to :music_session + validates :user, presence: true validates :message, length: {minimum: 1, maximum: 255}, no_profanity: true class << self diff --git a/ruby/lib/jam_ruby/models/claimed_recording.rb b/ruby/lib/jam_ruby/models/claimed_recording.rb index 036b2cd66..573d5a390 100644 --- a/ruby/lib/jam_ruby/models/claimed_recording.rb +++ b/ruby/lib/jam_ruby/models/claimed_recording.rb @@ -7,7 +7,7 @@ module JamRuby belongs_to :user, :class_name => "JamRuby::User", :inverse_of => :claimed_recordings belongs_to :genre, :class_name => "JamRuby::Genre" has_many :recorded_tracks, :through => :recording, :class_name => "JamRuby::RecordedTrack" - has_many :playing_sessions, :class_name => "JamRuby::MusicSession" + has_many :playing_sessions, :class_name => "JamRuby::ActiveMusicSession" has_many :likes, :class_name => "JamRuby::RecordingLiker", :foreign_key => "claimed_recording_id" has_many :plays, :class_name => "JamRuby::PlayablePlay", :foreign_key => "claimed_recording_id", :dependent => :destroy has_one :share_token, :class_name => "JamRuby::ShareToken", :inverse_of => :shareable, :foreign_key => 'shareable_id' diff --git a/ruby/lib/jam_ruby/models/connection.rb b/ruby/lib/jam_ruby/models/connection.rb index 8e9836c30..f5e2d315a 100644 --- a/ruby/lib/jam_ruby/models/connection.rb +++ b/ruby/lib/jam_ruby/models/connection.rb @@ -3,18 +3,20 @@ require 'aasm' module JamRuby class Connection < ActiveRecord::Base + # client_types + TYPE_CLIENT = 'client' + TYPE_BROWSER = 'browser' attr_accessor :joining_session self.primary_key = 'id' belongs_to :user, :class_name => "JamRuby::User" - belongs_to :music_session, :class_name => "JamRuby::MusicSession" + belongs_to :music_session, :class_name => "JamRuby::ActiveMusicSession", foreign_key: :music_session_id has_many :tracks, :class_name => "JamRuby::Track", :inverse_of => :connection, :foreign_key => 'connection_id', :dependent => :delete_all - validates :as_musician, :inclusion => {:in => [true, false]} - validates :client_type, :inclusion => {:in => ['client', 'browser']} + validates :client_type, :inclusion => {:in => [TYPE_CLIENT, TYPE_BROWSER]} validate :can_join_music_session, :if => :joining_session? after_save :require_at_least_one_track_when_in_session, :if => :joining_session? after_create :did_create diff --git a/ruby/lib/jam_ruby/models/diagnostic.rb b/ruby/lib/jam_ruby/models/diagnostic.rb new file mode 100644 index 000000000..a4be2dab5 --- /dev/null +++ b/ruby/lib/jam_ruby/models/diagnostic.rb @@ -0,0 +1,89 @@ +module JamRuby + class Diagnostic < ActiveRecord::Base + + # occurs when the client does not see a heartbeat from the server in a while + NO_HEARTBEAT_ACK = 'NO_HEARTBEAT_ACK' + + # occurs when the client sees the socket go down + WEBSOCKET_CLOSED_REMOTELY = 'WEBSOCKET_CLOSED_REMOTELY' + + # occurs when the client makes the socket go down + WEBSOCKET_CLOSED_LOCALLY = 'WEBSOCKET_CLOSED_LOCALLY' + + # occurs when the websocket-gateway has finally given up entirely on a connection with no heartbeats seen in a while + EXPIRED_STALE_CONNECTION = 'EXPIRED_STALE_CONNECTION' + + # occurs when the websocket-gateway is trying to handle a heartbeat, but can't find any state for the user. + # this implies a coding error + MISSING_CLIENT_STATE = 'MISSING_CLIENT_STATE' + + # websocket gateway did not recognize message. indicates out-of-date websocket-gateway + UNKNOWN_MESSAGE_TYPE = 'UNKNOWN_MESSAGE_TYPE' + + # empty route_to in message; which is invalid. indicates programming error + MISSING_ROUTE_TO = 'MISSING_ROUTE_TO' + + # websocket gateway got a client with the same client_id as an already-connected client + DUPLICATE_CLIENT = 'DUPLICATE_CLIENT' + + DIAGNOSTIC_TYPES = [NO_HEARTBEAT_ACK, WEBSOCKET_CLOSED_REMOTELY, EXPIRED_STALE_CONNECTION, + MISSING_CLIENT_STATE, UNKNOWN_MESSAGE_TYPE, MISSING_ROUTE_TO, + DUPLICATE_CLIENT, WEBSOCKET_CLOSED_LOCALLY] + + # creator types # + CLIENT = 'client' + WEBSOCKET_GATEWAY = 'websocket-gateway' + CREATORS = [CLIENT, WEBSOCKET_GATEWAY] + + self.primary_key = 'id' + self.inheritance_column = 'nothing' + + belongs_to :user, :inverse_of => :diagnostics, :class_name => "JamRuby::User", :foreign_key => "user_id" + + validates :user, :presence => true + validates :type, :inclusion => {:in => DIAGNOSTIC_TYPES} + validates :creator, :inclusion => {:in => CREATORS} + validates :data, length: {maximum: 100000} + + + def self.expired_stale_connection(user, context) + Diagnostic.save(EXPIRED_STALE_CONNECTION, user, WEBSOCKET_GATEWAY, context.to_json) if user + end + + def self.missing_client_state(user, context) + Diagnostic.save(MISSING_CLIENT_STATE, user, WEBSOCKET_GATEWAY, context.to_json) if user + end + + def self.missing_connection(user, context) + Diagnostic.save(MISSING_CONNECTION, user, WEBSOCKET_GATEWAY, context.to_json) if user + end + + def self.duplicate_client(user, context) + Diagnostic.save(DUPLICATE_CLIENT, user, WEBSOCKET_GATEWAY, context.to_json) if user + end + + def self.unknown_message_type(user, client_msg) + Diagnostic.save(UNKNOWN_MESSAGE_TYPE, user, WEBSOCKET_GATEWAY, client_msg.to_json) if user + end + + def self.missing_route_to(user, client_msg) + Diagnostic.save(MISSING_ROUTE_TO, user, WEBSOCKET_GATEWAY, client_msg.to_json) if user + end + + + def self.save(type, user, creator, data) + diagnostic = Diagnostic.new + if user.class == String + diagnostic.user_id = user + else + diagnostic.user = user + end + + diagnostic.data = data + diagnostic.type = type + diagnostic.creator = creator + diagnostic.save + end + end + +end diff --git a/ruby/lib/jam_ruby/models/email_batch.rb b/ruby/lib/jam_ruby/models/email_batch.rb index 74dff06ec..02c729dda 100644 --- a/ruby/lib/jam_ruby/models/email_batch.rb +++ b/ruby/lib/jam_ruby/models/email_batch.rb @@ -13,7 +13,7 @@ module JamRuby VAR_LAST_NAME = '@LASTNAME' DEFAULT_SENDER = "noreply@jamkazam.com" - BATCH_SIZE = 1000 + BATCH_SIZE = 5 BODY_TEMPLATE =< :sent_fan_invitations, :class_name => "JamRuby::User", :foreign_key => "sender_id" belongs_to :receiver, :inverse_of => :received_fan_invitations, :class_name => "JamRuby::User", :foreign_key => "receiver_id" - belongs_to :music_session, :inverse_of => :fan_invitations, :class_name => "JamRuby::MusicSession" + belongs_to :music_session, :inverse_of => :fan_invitations, :class_name => "JamRuby::MusicSession", :foreign_key => "music_session_id" validates :sender, :presence => true validates :receiver, :presence => true @@ -18,7 +18,7 @@ module JamRuby private def require_sender_in_music_session - unless music_session.users.exists? sender + unless music_session.part_of_session? sender errors.add(:music_session, MEMBERSHIP_REQUIRED_OF_MUSIC_SESSION) end end diff --git a/ruby/lib/jam_ruby/models/feed.rb b/ruby/lib/jam_ruby/models/feed.rb index 64f002a1d..7c118d364 100644 --- a/ruby/lib/jam_ruby/models/feed.rb +++ b/ruby/lib/jam_ruby/models/feed.rb @@ -3,12 +3,12 @@ module JamRuby class Feed < ActiveRecord::Base belongs_to :recording, class_name: "JamRuby::Recording", inverse_of: :feed, foreign_key: 'recording_id' - belongs_to :music_session_history, class_name: "JamRuby::MusicSessionHistory", inverse_of: :feed, foreign_key: 'music_session_id' + belongs_to :music_session, class_name: "JamRuby::MusicSession", inverse_of: :feed, foreign_key: 'music_session_id' FIXNUM_MAX = (2**(0.size * 8 -2) -1) SORT_TYPES = ['date', 'plays', 'likes'] TIME_RANGES = { "today" => 1 , "week" => 7, "month" => 30, "all" => 0} - TYPE_FILTERS = ['music_session_history', 'recording', 'all'] + TYPE_FILTERS = ['music_session', 'recording', 'all'] def self.index(user, params = {}) limit = params[:limit] @@ -39,9 +39,9 @@ module JamRuby target_user = params[:user] target_band = params[:band] - #query = Feed.includes([:recording]).includes([:music_session_history]).limit(limit) + #query = Feed.includes([:recording]).includes([:music_session]).limit(limit) query = Feed.joins("LEFT OUTER JOIN recordings ON recordings.id = feeds.recording_id") - .joins("LEFT OUTER JOIN music_sessions_history ON music_sessions_history.id = feeds.music_session_id") + .joins("LEFT OUTER JOIN music_sessions ON music_sessions.id = feeds.music_session_id") .limit(limit) # handle sort @@ -50,10 +50,10 @@ module JamRuby query = query.order('feeds.id DESC') elsif sort == 'plays' query = query.offset(start) - query = query.order("COALESCE(recordings.play_count, music_sessions_history.play_count) DESC ") + query = query.order("COALESCE(recordings.play_count, music_sessions.play_count) DESC ") elsif sort == 'likes' query = query.offset(start) - query = query.order("COALESCE(recordings.like_count, music_sessions_history.like_count) DESC ") + query = query.order("COALESCE(recordings.like_count, music_sessions.like_count) DESC ") else raise "sort not implemented: #{sort}" end @@ -65,7 +65,7 @@ module JamRuby end # handle type filters - if type_filter == 'music_session_history' + if type_filter == 'music_session' query = query.where('feeds.music_session_id is not NULL') elsif type_filter == 'recording' query = query.where('feeds.recording_id is not NULL') @@ -76,48 +76,48 @@ module JamRuby if target_user != user.id require_public_recordings = "claimed_recordings.is_public = TRUE AND" - require_public_sessions = "music_sessions_history.fan_access = TRUE AND" + require_public_sessions = "music_sessions.fan_access = TRUE AND" end query = query.joins("LEFT OUTER JOIN claimed_recordings ON recordings.id = claimed_recordings.recording_id AND #{require_public_recordings} (claimed_recordings.user_id = '#{target_user}' OR (recordings.band_id IN (SELECT band_id FROM bands_musicians where user_id='#{target_user}')))") - query = query.joins("LEFT OUTER JOIN music_sessions_user_history ON music_sessions_history.id = music_sessions_user_history.music_session_id AND #{require_public_sessions} music_sessions_user_history.user_id = '#{target_user}'") - query = query.group("feeds.id, feeds.recording_id, feeds.music_session_id, feeds.created_at, feeds.updated_at, recordings.id, music_sessions_history.id") + query = query.joins("LEFT OUTER JOIN music_sessions_user_history ON music_sessions.id = music_sessions_user_history.music_session_id AND #{require_public_sessions} music_sessions_user_history.user_id = '#{target_user}'") + query = query.group("feeds.id, feeds.recording_id, feeds.music_session_id, feeds.created_at, feeds.updated_at, recordings.id, music_sessions.id") if sort == 'plays' - query = query.group("COALESCE(recordings.play_count, music_sessions_history.play_count)") + query = query.group("COALESCE(recordings.play_count, music_sessions.play_count)") elsif sort == 'likes' - query = query.group("COALESCE(recordings.like_count, music_sessions_history.like_count)") + query = query.group("COALESCE(recordings.like_count, music_sessions.like_count)") end query = query.where('recordings.id is NULL OR claimed_recordings.id IS NOT NULL') - query = query.where('music_sessions_history.id is NULL OR music_sessions_user_history.id IS NOT NULL') + query = query.where('music_sessions.id is NULL OR music_sessions_user_history.id IS NOT NULL') elsif target_band unless Band.find(target_band).users.include?(user) require_public_recordings = "claimed_recordings.is_public = TRUE AND" - require_public_sessions = "music_sessions_history.fan_access = TRUE AND" + require_public_sessions = "music_sessions.fan_access = TRUE AND" end query = query.joins("LEFT OUTER JOIN claimed_recordings ON recordings.id = claimed_recordings.recording_id AND #{require_public_recordings} recordings.band_id = '#{target_band}'") - query = query.where("music_sessions_history IS NULL OR #{require_public_sessions} music_sessions_history.band_id = '#{target_band}'") - query = query.group("feeds.id, feeds.recording_id, feeds.music_session_id, feeds.created_at, feeds.updated_at, recordings.id, music_sessions_history.id") + query = query.where("music_sessions IS NULL OR #{require_public_sessions} music_sessions.band_id = '#{target_band}'") + query = query.group("feeds.id, feeds.recording_id, feeds.music_session_id, feeds.created_at, feeds.updated_at, recordings.id, music_sessions.id") if sort == 'plays' - query = query.group("COALESCE(recordings.play_count, music_sessions_history.play_count)") + query = query.group("COALESCE(recordings.play_count, music_sessions.play_count)") elsif sort == 'likes' - query = query.group("COALESCE(recordings.like_count, music_sessions_history.like_count)") + query = query.group("COALESCE(recordings.like_count, music_sessions.like_count)") end query = query.where('recordings.id is NULL OR claimed_recordings.id IS NOT NULL') - #query = query.where('music_sessions_history.id is NULL OR music_sessions_user_history.id IS NOT NULL') + #query = query.where('music_sessions.id is NULL OR music_sessions_user_history.id IS NOT NULL') else query = query.joins('LEFT OUTER JOIN claimed_recordings ON recordings.id = claimed_recordings.recording_id AND claimed_recordings.is_public = TRUE') - query = query.joins("LEFT OUTER JOIN music_sessions_user_history ON music_sessions_history.id = music_sessions_user_history.music_session_id AND music_sessions_history.fan_access = TRUE") - query = query.group("feeds.id, feeds.recording_id, feeds.music_session_id, feeds.created_at, feeds.updated_at, recordings.id, music_sessions_history.id") + query = query.joins("LEFT OUTER JOIN music_sessions_user_history ON music_sessions.id = music_sessions_user_history.music_session_id AND music_sessions.fan_access = TRUE") + query = query.group("feeds.id, feeds.recording_id, feeds.music_session_id, feeds.created_at, feeds.updated_at, recordings.id, music_sessions.id") if sort == 'plays' - query = query.group("COALESCE(recordings.play_count, music_sessions_history.play_count)") + query = query.group("COALESCE(recordings.play_count, music_sessions.play_count)") elsif sort == 'likes' - query = query.group("COALESCE(recordings.like_count, music_sessions_history.like_count)") + query = query.group("COALESCE(recordings.like_count, music_sessions.like_count)") end query = query.where('recordings.id is NULL OR claimed_recordings.is_public = TRUE') - query = query.where('music_sessions_history.id is NULL OR music_sessions_user_history.id IS NOT NULL') + query = query.where('music_sessions.id is NULL OR music_sessions_user_history.id IS NOT NULL') end diff --git a/ruby/lib/jam_ruby/models/friend_request.rb b/ruby/lib/jam_ruby/models/friend_request.rb index b2fe2f5ba..ca5a8c978 100644 --- a/ruby/lib/jam_ruby/models/friend_request.rb +++ b/ruby/lib/jam_ruby/models/friend_request.rb @@ -34,7 +34,7 @@ module JamRuby ActiveRecord::Base.transaction do friend_request = FriendRequest.find(id) friend_request.status = status - friend_request.updated_at = Time.now.getutc + friend_request.updated_at = Time.now friend_request.save # create both records for this friendship diff --git a/ruby/lib/jam_ruby/models/genre.rb b/ruby/lib/jam_ruby/models/genre.rb index f2582fe94..b8e61a270 100644 --- a/ruby/lib/jam_ruby/models/genre.rb +++ b/ruby/lib/jam_ruby/models/genre.rb @@ -7,11 +7,13 @@ module JamRuby has_many :band_genres, class_name: "JamRuby::BandGenre" has_many :bands, class_name: "JamRuby::Band", :through => :band_genres + + # music sessions + has_many :music_sessions, :class_name => "JamRuby::MusicSession" + # genres has_and_belongs_to_many :recordings, :class_name => "JamRuby::Recording", :join_table => "recordings_genres" - # music sessions - has_and_belongs_to_many :music_sessions, :class_name => "JamRuby::MusicSession", :join_table => "genres_music_sessions" def to_s description diff --git a/ruby/lib/jam_ruby/models/icecast_mount.rb b/ruby/lib/jam_ruby/models/icecast_mount.rb index 97ca69431..4e07dc786 100644 --- a/ruby/lib/jam_ruby/models/icecast_mount.rb +++ b/ruby/lib/jam_ruby/models/icecast_mount.rb @@ -11,7 +11,7 @@ module JamRuby :sourced_needs_changing_at, as: :admin belongs_to :authentication, class_name: "JamRuby::IcecastUserAuthentication", inverse_of: :mount, :foreign_key => 'authentication_id' - belongs_to :music_session, class_name: "JamRuby::MusicSession", inverse_of: :mount, foreign_key: 'music_session_id' + belongs_to :music_session, class_name: "JamRuby::ActiveMusicSession", inverse_of: :mount, foreign_key: 'music_session_id' belongs_to :server, class_name: "JamRuby::IcecastServer", inverse_of: :mounts, foreign_key: 'icecast_server_id' belongs_to :mount_template, class_name: "JamRuby::IcecastMountTemplate", inverse_of: :mounts, foreign_key: 'icecast_mount_template_id' @@ -76,7 +76,7 @@ module JamRuby end # creates a templated - def self.build_session_mount(music_session, icecast_server) + def self.build_session_mount(music_session, active_music_session, icecast_server) # only public sessions get mounts currently return nil unless music_session.fan_access @@ -84,7 +84,7 @@ module JamRuby mount = nil if icecast_server && icecast_server.mount_template_id # we have a server with an associated mount_template; we can create a mount automatically - mount = icecast_server.mount_template.build_session_mount(music_session) + mount = icecast_server.mount_template.build_session_mount(music_session, active_music_session) mount.server = icecast_server end mount diff --git a/ruby/lib/jam_ruby/models/icecast_mount_template.rb b/ruby/lib/jam_ruby/models/icecast_mount_template.rb index 64c995938..c93dc1c8a 100644 --- a/ruby/lib/jam_ruby/models/icecast_mount_template.rb +++ b/ruby/lib/jam_ruby/models/icecast_mount_template.rb @@ -44,7 +44,7 @@ module JamRuby end # pick a server that's in the same group as the user that is under the least load - def build_session_mount(music_session) + def build_session_mount(music_session, active_music_session) mount = IcecastMount.new mount.authentication = authentication mount.mount_template = self @@ -55,7 +55,7 @@ module JamRuby mount.stream_name = "JamKazam music session created by #{music_session.creator.name}" mount.stream_description = music_session.description mount.stream_url = "http://www.jamkazam.com" ## TODO/XXX, the jamkazam url should be the page hosting the widget - mount.genre = music_session.genres.map {|genre| genre.description}.join(',') + mount.genre = music_session.genre.description mount end end diff --git a/ruby/lib/jam_ruby/models/instrument.rb b/ruby/lib/jam_ruby/models/instrument.rb index f8f7f5daa..e7f0185f5 100644 --- a/ruby/lib/jam_ruby/models/instrument.rb +++ b/ruby/lib/jam_ruby/models/instrument.rb @@ -41,7 +41,7 @@ module JamRuby has_many :recorded_tracks, :class_name => "JamRuby::RecordedTrack", :inverse_of => :instrument # music sessions - has_and_belongs_to_many :music_sessions, :class_name => "JamRuby::MusicSession", :join_table => "genres_music_sessions" + has_and_belongs_to_many :music_sessions, :class_name => "JamRuby::ActiveMusicSession", :join_table => "genres_music_sessions" def self.standard_list return Instrument.where('instruments.popularity > 0').order('instruments.popularity DESC, instruments.description ASC') diff --git a/ruby/lib/jam_ruby/models/invitation.rb b/ruby/lib/jam_ruby/models/invitation.rb index 9bd998d56..bf822971f 100644 --- a/ruby/lib/jam_ruby/models/invitation.rb +++ b/ruby/lib/jam_ruby/models/invitation.rb @@ -8,7 +8,7 @@ module JamRuby self.primary_key = 'id' belongs_to :sender, :inverse_of => :sent_invitations, :class_name => "JamRuby::User", :foreign_key => "sender_id" belongs_to :receiver, :inverse_of => :received_invitations, :class_name => "JamRuby::User", :foreign_key => "receiver_id" - belongs_to :music_session, :inverse_of => :invitations, :class_name => "JamRuby::MusicSession" + belongs_to :music_session, :inverse_of => :invitations, :class_name => "JamRuby::MusicSession", :foreign_key => "music_session_id" belongs_to :join_request, :inverse_of => :invitations, :class_name => "JamRuby::JoinRequest" validates :sender, :presence => true @@ -20,7 +20,7 @@ module JamRuby private def require_sender_in_music_session - unless music_session.users.exists? sender + unless music_session.part_of_session? sender errors.add(:music_session, MEMBERSHIP_REQUIRED_OF_MUSIC_SESSION) end end diff --git a/ruby/lib/jam_ruby/models/join_request.rb b/ruby/lib/jam_ruby/models/join_request.rb index 4ebbd64ba..d7c4f1f28 100644 --- a/ruby/lib/jam_ruby/models/join_request.rb +++ b/ruby/lib/jam_ruby/models/join_request.rb @@ -6,7 +6,7 @@ module JamRuby self.primary_key = 'id' belongs_to :user, :class_name => "JamRuby::User" - belongs_to :music_session, :class_name => "JamRuby::MusicSession" + belongs_to :music_session, :class_name => "JamRuby::MusicSession", :foreign_key => "music_session_id" has_many :invitations, :inverse_of => :join_request, :class_name => "JamRuby::Invitation" validates :user, :presence => true diff --git a/ruby/lib/jam_ruby/models/music_session.rb b/ruby/lib/jam_ruby/models/music_session.rb index 9595eb163..7a98d0e45 100644 --- a/ruby/lib/jam_ruby/models/music_session.rb +++ b/ruby/lib/jam_ruby/models/music_session.rb @@ -1,39 +1,32 @@ module JamRuby class MusicSession < ActiveRecord::Base + + attr_accessor :legal_terms + + self.table_name = "music_sessions" + self.primary_key = 'id' - attr_accessor :legal_terms, :skip_genre_validation, :max_score - attr_accessible :creator, :description, :musician_access, :approval_required, :fan_chat, :fan_access, :genres + belongs_to :creator,:class_name => 'JamRuby::User', :foreign_key => :user_id, :inverse_of => :music_session_histories - belongs_to :creator, :inverse_of => :music_sessions, :class_name => "JamRuby::User", :foreign_key => "user_id" - belongs_to :claimed_recording, :class_name => "JamRuby::ClaimedRecording", :foreign_key => "claimed_recording_id", :inverse_of => :playing_sessions - belongs_to :claimed_recording_initiator, :class_name => "JamRuby::User", :inverse_of => :playing_claimed_recordings, :foreign_key => "claimed_recording_initiator_id" + belongs_to :band, :class_name => 'JamRuby::Band', :foreign_key => :band_id, :inverse_of => :music_sessions - has_one :music_session_history, :class_name => "JamRuby::MusicSessionHistory" - has_one :mount, :class_name => "JamRuby::IcecastMount", :inverse_of => :music_session, :foreign_key => 'music_session_id' + belongs_to :active_music_session, :class_name => 'JamRuby::ActiveMusicSession', foreign_key: :music_session_id - has_many :connections, :class_name => "JamRuby::Connection" - has_many :users, :through => :connections, :class_name => "JamRuby::User" - has_and_belongs_to_many :genres, :class_name => "::JamRuby::Genre", :join_table => "genres_music_sessions" - has_many :join_requests, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::JoinRequest" - has_many :invitations, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::Invitation" + has_many :music_session_user_histories, :class_name => "JamRuby::MusicSessionUserHistory", :foreign_key => "music_session_id", :dependent => :delete_all + has_many :comments, :class_name => "JamRuby::MusicSessionComment", :foreign_key => "music_session_id" + has_many :likes, :class_name => "JamRuby::MusicSessionLiker", :foreign_key => "session_id" + has_many :plays, :class_name => "JamRuby::PlayablePlay", :as => :playable, :dependent => :destroy + has_one :share_token, :class_name => "JamRuby::ShareToken", :inverse_of => :shareable, :foreign_key => 'shareable_id' + has_one :feed, :class_name => "JamRuby::Feed", :inverse_of => :music_session, :foreign_key => 'music_session_id', :dependent => :destroy + belongs_to :genre, :class_name => "JamRuby::Genre", :inverse_of => :music_sessions, :foreign_key => 'genre_id' + has_many :join_requests, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::JoinRequest", :foreign_key => "music_session_id" + has_many :invitations, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::Invitation", :foreign_key => "music_session_id" has_many :invited_musicians, :through => :invitations, :class_name => "JamRuby::User", :foreign_key => "receiver_id", :source => :receiver - - has_many :fan_invitations, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::FanInvitation" + has_many :fan_invitations, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::FanInvitation", :foreign_key => "music_session_id" has_many :invited_fans, :through => :fan_invitations, :class_name => "JamRuby::User", :foreign_key => "receiver_id", :source => :receiver - has_many :recordings, :class_name => "JamRuby::Recording", :inverse_of => :music_session - has_many :chats, :class_name => "JamRuby::ChatMessages", :foreign_key => "session_id" - belongs_to :band, :inverse_of => :music_sessions, :class_name => "JamRuby::Band", :foreign_key => "band_id" - - after_create :started_session - - validate :require_at_least_one_genre, :limit_max_genres - after_save :sync_music_session_history - - after_destroy do |obj| - JamRuby::MusicSessionHistory.removed_music_session(obj.id) - end + validates :genre, :presence => true validates :description, :presence => true, :no_profanity => true validates :fan_chat, :inclusion => {:in => [true, false]} validates :fan_access, :inclusion => {:in => [true, false]} @@ -42,400 +35,184 @@ module JamRuby validates :legal_terms, :inclusion => {:in => [true]}, :on => :create validates :creator, :presence => true validate :creator_is_musician - validate :no_new_playback_while_playing - #default_scope :select => "*, 0 as score" + before_create :generate_share_token + before_create :add_to_feed - def attributes - super.merge('max_score' => self.max_score) + SHARE_TOKEN_LENGTH = 8 + + SEPARATOR = '|' + + def add_to_feed + feed = Feed.new + feed.music_session = self end - def max_score - nil unless has_attribute?(:max_score) - read_attribute(:max_score).to_i + def comment_count + self.comments.size end - before_create :create_uuid - def create_uuid - #self.id = SecureRandom.uuid - end - - def before_destroy - self.mount.destroy if self.mount - end - - def creator_is_musician - unless creator.musician? - errors.add(:creator, ValidationMessages::MUST_BE_A_MUSICIAN) - end - end - - def no_new_playback_while_playing - # if we previous had a claimed recording and are trying to set one - # and if also the previous initiator is different than the current one... it's a no go - if !claimed_recording_id_was.nil? && !claimed_recording_id.nil? && - claimed_recording_initiator_id_was != claimed_recording_initiator_id - errors.add(:claimed_recording, ValidationMessages::CLAIMED_RECORDING_ALREADY_IN_PROGRESS) - end - end - - # returns an array of client_id's that are in this session - # if as_musician is nil, all connections in the session ,regardless if it's a musician or not or not - # you can also exclude a client_id from the returned set by setting exclude_client_id - def get_connection_ids(options = {}) - as_musician = options[:as_musician] - exclude_client_id = options[:exclude_client_id] - - where = { :music_session_id => self.id } - where[:as_musician] = as_musician unless as_musician.nil? - - exclude = "client_id != '#{exclude_client_id}'"unless exclude_client_id.nil? - - Connection.select(:client_id).where(where).where(exclude).map(&:client_id) - end - - # This is a little confusing. You can specify *BOTH* friends_only and my_bands_only to be true - # If so, then it's an OR condition. If both are false, you can get sessions with anyone. - def self.index(current_user, options = {}) - participants = options[:participants] - genres = options[:genres] - keyword = options[:keyword] - friends_only = options[:friends_only].nil? ? false : options[:friends_only] - my_bands_only = options[:my_bands_only].nil? ? false : options[:my_bands_only] - as_musician = options[:as_musician].nil? ? true : options[:as_musician] - - query = MusicSession - .joins( - %Q{ - INNER JOIN - connections - ON - music_sessions.id = connections.music_session_id - } - ) - .joins( - %Q{ - LEFT OUTER JOIN - friendships - ON - connections.user_id = friendships.user_id - AND - friendships.friend_id = '#{current_user.id}' - } - ) - .joins( - %Q{ - LEFT OUTER JOIN - invitations - ON - invitations.music_session_id = music_sessions.id - AND - invitations.receiver_id = '#{current_user.id}' - } - ) - .group( - %Q{ - music_sessions.id - } - ) - .order( - %Q{ - SUM(CASE WHEN invitations.id IS NULL THEN 0 ELSE 1 END) DESC, - SUM(CASE WHEN friendships.user_id IS NULL THEN 0 ELSE 1 END) DESC, - music_sessions.created_at DESC - } - ) - - if as_musician - query = query.where( - %Q{ - musician_access = true - OR - invitations.id IS NOT NULL - } - ) - else - # if you are trying to join the session as a fan/listener, - # we have to have a mount, fan_access has to be true, and we have to allow for the reload of icecast to have taken effect - query = query.joins('INNER JOIN icecast_mounts ON icecast_mounts.music_session_id = music_sessions.id INNER JOIN icecast_servers ON icecast_mounts.icecast_server_id = icecast_servers.id') - query = query.where(:fan_access => true) - query = query.where("(music_sessions.created_at < icecast_servers.config_updated_at)") - end - - query = query.where("music_sessions.description like '%#{keyword}%'") unless keyword.nil? - query = query.where("connections.user_id" => participants.split(',')) unless participants.nil? - query = query.joins(:genres).where("genres.id" => genres.split(',')) unless genres.nil? - - if my_bands_only - query = query.joins( - %Q{ - LEFT OUTER JOIN - bands_musicians - ON - bands_musicians.user_id = '#{current_user.id}' - } - ) - end - - if my_bands_only || friends_only - query = query.where( - %Q{ - #{friends_only ? "friendships.user_id IS NOT NULL" : "false"} - OR - #{my_bands_only ? "bands_musicians.band_id = music_sessions.band_id" : "false"} - } - ) - end - - return query - end - - # This is a little confusing. You can specify *BOTH* friends_only and my_bands_only to be true - # If so, then it's an OR condition. If both are false, you can get sessions with anyone. - # note, this is mostly the same as above but includes paging through the result and and scores. - # thus it needs the client_id... - def self.nindex(current_user, options = {}) - client_id = options[:client_id] - participants = options[:participants] - genres = options[:genres] - keyword = options[:keyword] - friends_only = options[:friends_only].nil? ? false : options[:friends_only] - my_bands_only = options[:my_bands_only].nil? ? false : options[:my_bands_only] - as_musician = options[:as_musician].nil? ? true : options[:as_musician] - offset = options[:offset] - limit = options[:limit] - - connection = Connection.where(client_id: client_id).first! - locidispid = connection.locidispid - - query = MusicSession - .select("music_sessions.*, max(coalesce(current_scores.score, 1000)) as max_score") # 1000 is higher than the allowed max of 999 - .joins( - %Q{ - INNER JOIN - connections - ON - music_sessions.id = connections.music_session_id - } - ) - .joins( - %Q{ - LEFT OUTER JOIN - current_scores - ON - current_scores.alocidispid = connections.locidispid - AND - current_scores.blocidispid = #{locidispid} - } - ) - .joins( - %Q{ - LEFT OUTER JOIN - friendships - ON - connections.user_id = friendships.user_id - AND - friendships.friend_id = '#{current_user.id}' - } - ) - .joins( - %Q{ - LEFT OUTER JOIN - invitations - ON - invitations.music_session_id = music_sessions.id - AND - invitations.receiver_id = '#{current_user.id}' - } - ) - .group( - %Q{ - music_sessions.id - } - ) - .order( - %Q{ - SUM(CASE WHEN invitations.id IS NULL THEN 0 ELSE 1 END) DESC, - SUM(CASE WHEN friendships.user_id IS NULL THEN 0 ELSE 1 END) DESC, - music_sessions.created_at DESC - } - ) - - if (offset) - query = query.offset(offset) - end - - if (limit) - query = query.limit(limit) - end - - if as_musician - query = query.where( - %Q{ - musician_access = true - OR - music_sessions.user_id = '#{current_user.id}' - OR - invitations.id IS NOT NULL - } - ) - else - # if you are trying to join the session as a fan/listener, - # we have to have a mount, fan_access has to be true, and we have to allow for the reload of icecast to have taken effect - query = query.joins('INNER JOIN icecast_mounts ON icecast_mounts.music_session_id = music_sessions.id INNER JOIN icecast_servers ON icecast_mounts.icecast_server_id = icecast_servers.id') - query = query.where(:fan_access => true) - query = query.where("(music_sessions.created_at < icecast_servers.config_updated_at)") - end - - query = query.where("music_sessions.description like '%#{keyword}%'") unless keyword.nil? - query = query.where("connections.user_id" => participants.split(',')) unless participants.nil? - query = query.joins(:genres).where("genres.id" => genres.split(',')) unless genres.nil? - - if my_bands_only - query = query.joins( - %Q{ - LEFT OUTER JOIN - bands_musicians - ON - bands_musicians.user_id = '#{current_user.id}' - } - ) - end - - if my_bands_only || friends_only - query = query.where( - %Q{ - #{friends_only ? "friendships.user_id IS NOT NULL" : "false"} - OR - #{my_bands_only ? "bands_musicians.band_id = music_sessions.band_id" : "false"} - } - ) - end - - return query - end - - # Verifies that the specified user can join this music session - def can_join? user, as_musician - if as_musician - if !user.musician - return false # "a fan can not join a music session as a musician" - raise PermissionError, "a fan can not join a music session as a musician" - end - - if self.musician_access - if self.approval_required - return self.invited_musicians.exists?(user) - else - return true + def grouped_tracks + tracks = [] + self.music_session_user_histories.each do |msuh| + user = User.find(msuh.user_id) + t = Track.new + t.musician = user + t.instrument_ids = [] + # this treats each track as a "user", which has 1 or more instruments in the session + unless msuh.instruments.blank? + instruments = msuh.instruments.split(SEPARATOR) + instruments.each do |instrument| + if !t.instrument_ids.include? instrument + t.instrument_ids << instrument + end end - - else - # the creator can always join, and the invited users can join - return self.creator == user || self.invited_musicians.exists?(user) end - else - # it's a fan, and the only way a fan can join is if fan_access is true - return self.fan_access + tracks << t + end + tracks + end + + def self.index(current_user, user_id, band_id = nil, genre = nil) + hide_private = false + if current_user.id != user_id + hide_private = false # TODO: change to true once public flag exists + end + + query = MusicSession + .joins( + %Q{ + LEFT OUTER JOIN + music_sessions_user_history + ON + music_sessions.id = music_sessions_user_history.music_session_id + } + ) + .where( + %Q{ + music_sessions.user_id = '#{user_id}' + } + ) + + #query = query.where("public = false") unless !hide_private + query = query.where("music_sessions.band_id = '#{band_id}") unless band_id.nil? + query = query.where("music_sessions.genres like '%#{genre}%'") unless genre.nil? + return query + end + + def unique_users + User + .joins(:music_session_user_histories) + .group("users.id") + .order("users.id") + .where(%Q{ music_sessions_user_history.music_session_id = '#{id}'}) + end + + # returns one user history per user, with instruments all crammed together, and with total duration + def unique_user_histories + MusicSessionUserHistory + .joins(:user) + .select("STRING_AGG(instruments, '|') AS total_instruments, + SUM(date_part('epoch', COALESCE(music_sessions_user_history.session_removed_at, music_sessions_user_history.created_at) - music_sessions_user_history.created_at)) AS total_duration, + music_sessions_user_history.user_id, music_sessions_user_history.music_session_id, users.first_name, users.last_name, users.photo_url") + .group("music_sessions_user_history.user_id, music_sessions_user_history.music_session_id, users.first_name, users.last_name, users.photo_url") + .order("music_sessions_user_history.user_id") + .where(%Q{ music_sessions_user_history.music_session_id = '#{id}'}) + end + + def duration_minutes + end_time = self.session_removed_at || Time.now + (end_time - self.created_at) / 60.0 + end + + def music_session_user_histories + @msuh ||= JamRuby::MusicSessionUserHistory + .where(:music_session_id => self.id) + .order('created_at DESC') + end + + def comments + @comments ||= JamRuby::MusicSessionComment + .where(:music_session_id => self.id) + .order('created_at DESC') + end + + def likes + @likes ||= JamRuby::MusicSessionLiker + .where(:music_session_id => self.music_session_id) + end + + # these are 'users that are a part of this session' + # which means are currently in the music_session, or, rsvp'ed, or creator + def part_of_session? user + # XXX check RSVP'ed + user == self.creator || (active_music_session ? active_music_session.users.exists?(user) : false) + end + + def is_over? + active_music_session.nil? + end + + def has_mount? + active_music_session && active_music_session.mount + end + + def recordings + Recording.where(music_session_id: self.id) + end + + def end_history + self.update_attribute(:session_removed_at, Time.now) + + + # ensure all user histories are closed + music_session_user_histories.each do |music_session_user_history| + music_session_user_history.end_history + + # then update any users that need their user progress updated + if music_session_user_history.duration_minutes > 15 && music_session_user_history.max_concurrent_connections >= 3 + music_session_user_history.user.update_progression_field(:first_real_music_session_at) + end end end - # Verifies that the specified user can see this music session - def can_see? user - if self.musician_access || self.fan_access - return true - else - return self.creator == user || self.invited_musicians.exists?(user) - end + def self.removed_music_session(session_id) + hist = self + .where(:id => session_id) + .limit(1) + .first + + hist.end_history if hist + + Notification.send_session_ended(session_id) end - # Verifies that the specified user can delete this music session - def can_delete? user - # the creator can delete - return self.creator == user - end - - def access? user - return self.users.exists? user - end - - def most_recent_recording - recordings.where(:music_session_id => self.id).order('created_at desc').limit(1).first - end - - # is this music session currently recording? - def is_recording? - recordings.where(:duration => nil).count > 0 - end - - def is_playing_recording? - !self.claimed_recording.nil? - end - - def recording - recordings.where(:duration => nil).first - end - - # stops any active recording - def stop_recording - current_recording = self.recording - current_recording.stop unless current_recording.nil? - end - - def claimed_recording_start(owner, claimed_recording) - self.claimed_recording = claimed_recording - self.claimed_recording_initiator = owner - self.save - end - - def claimed_recording_stop - self.claimed_recording = nil - self.claimed_recording_initiator = nil - self.save - end - - def to_s - description - end - - def tick_track_changes - self.track_changes_counter += 1 - self.save!(:validate => false) - end - - def connected_participant_count - Connection.where(:music_session_id => self.id, - :aasm_state => Connection::CONNECT_STATE.to_s, - :as_musician => true) - .count - end - - def started_session - GoogleAnalyticsEvent.track_session_duration(self) - GoogleAnalyticsEvent.track_band_real_session(self) + def remove_non_alpha_num(token) + token.gsub(/[^0-9A-Za-z]/, '') end private - def require_at_least_one_genre - unless skip_genre_validation - if self.genres.length < Limits::MIN_GENRES_PER_SESSION - errors.add(:genres, ValidationMessages::SESSION_GENRE_MINIMUM_NOT_MET) - end + def generate_share_token + + token = loop do + token = SecureRandom.urlsafe_base64(SHARE_TOKEN_LENGTH, false) + token = remove_non_alpha_num(token) + token.upcase! + break token unless ShareToken.exists?(token: token) + end + + self.share_token = ShareToken.new + self.share_token.token = token + self.share_token.shareable_type = "session" + end + + def creator_is_musician + unless creator && creator.musician? + errors.add(:creator, ValidationMessages::MUST_BE_A_MUSICIAN) end end - def limit_max_genres - unless skip_genre_validation - if self.genres.length > Limits::MAX_GENRES_PER_SESSION - errors.add(:genres, ValidationMessages::SESSION_GENRE_LIMIT_EXCEEDED) - end - end - end - - def sync_music_session_history - MusicSessionHistory.save(self) - end end end diff --git a/ruby/lib/jam_ruby/models/music_session_comment.rb b/ruby/lib/jam_ruby/models/music_session_comment.rb index b23383b33..72cc94178 100644 --- a/ruby/lib/jam_ruby/models/music_session_comment.rb +++ b/ruby/lib/jam_ruby/models/music_session_comment.rb @@ -7,8 +7,8 @@ module JamRuby default_scope order('created_at DESC') - belongs_to(:music_session_history, - :class_name => "JamRuby::MusicSessionHistory", + belongs_to(:music_session, + :class_name => "JamRuby::MusicSession", :foreign_key => "music_session_id") belongs_to(:user, diff --git a/ruby/lib/jam_ruby/models/music_session_history.rb b/ruby/lib/jam_ruby/models/music_session_history.rb deleted file mode 100644 index 5e3637284..000000000 --- a/ruby/lib/jam_ruby/models/music_session_history.rb +++ /dev/null @@ -1,212 +0,0 @@ -module JamRuby - class MusicSessionHistory < ActiveRecord::Base - - self.table_name = "music_sessions_history" - - self.primary_key = 'id' - - belongs_to(:user, - :class_name => 'JamRuby::User', - :foreign_key => :user_id, - :inverse_of => :music_session_histories) - - belongs_to(:band, - :class_name => 'JamRuby::Band', - :foreign_key => :band_id, - :inverse_of => :music_session_history) - - belongs_to(:music_session, - :class_name => 'JamRuby::MusicSession', - :foreign_key => 'music_session_id') - - has_many :music_session_user_histories, :class_name => "JamRuby::MusicSessionUserHistory", :foreign_key => "music_session_id", :dependent => :delete_all - has_many :comments, :class_name => "JamRuby::MusicSessionComment", :foreign_key => "music_session_id" - has_many :likes, :class_name => "JamRuby::MusicSessionLiker", :foreign_key => "session_id" - has_many :plays, :class_name => "JamRuby::PlayablePlay", :as => :playable, :dependent => :destroy - has_one :share_token, :class_name => "JamRuby::ShareToken", :inverse_of => :shareable, :foreign_key => 'shareable_id' - has_one :feed, :class_name => "JamRuby::Feed", :inverse_of => :music_session_history, :foreign_key => 'music_session_id', :dependent => :destroy - - - before_create :generate_share_token - before_create :add_to_feed - - SHARE_TOKEN_LENGTH = 8 - - SEPARATOR = '|' - - def add_to_feed - feed = Feed.new - feed.music_session_history = self - end - - def comment_count - self.comments.size - end - - def grouped_tracks - tracks = [] - self.music_session_user_histories.each do |msuh| - user = User.find(msuh.user_id) - t = Track.new - t.musician = user - t.instrument_ids = [] - # this treats each track as a "user", which has 1 or more instruments in the session - unless msuh.instruments.blank? - instruments = msuh.instruments.split(SEPARATOR) - instruments.each do |instrument| - if !t.instrument_ids.include? instrument - t.instrument_ids << instrument - end - end - end - tracks << t - end - tracks - end - - def self.index(current_user, user_id, band_id = nil, genre = nil) - hide_private = false - if current_user.id != user_id - hide_private = false # TODO: change to true once public flag exists - end - - query = MusicSessionHistory - .joins( - %Q{ - LEFT OUTER JOIN - music_sessions_user_history - ON - music_sessions_history.music_session_id = music_sessions_user_history.music_session_id - } - ) - .where( - %Q{ - music_sessions_history.user_id = '#{user_id}' - } - ) - - #query = query.where("public = false") unless !hide_private - query = query.where("music_sessions_history.band_id = '#{band_id}") unless band_id.nil? - query = query.where("music_sessions_history.genres like '%#{genre}%'") unless genre.nil? - return query - end - - def unique_users - User - .joins(:music_session_user_histories) - .group("users.id") - .order("users.id") - .where(%Q{ music_sessions_user_history.music_session_id = '#{music_session_id}'}) - end - - # returns one user history per user, with instruments all crammed together, and with total duration - def unique_user_histories - MusicSessionUserHistory - .joins(:user) - .select("STRING_AGG(instruments, '|') AS total_instruments, - SUM(date_part('epoch', COALESCE(music_sessions_user_history.session_removed_at, music_sessions_user_history.created_at) - music_sessions_user_history.created_at)) AS total_duration, - music_sessions_user_history.user_id, music_sessions_user_history.music_session_id, users.first_name, users.last_name, users.photo_url") - .group("music_sessions_user_history.user_id, music_sessions_user_history.music_session_id, users.first_name, users.last_name, users.photo_url") - .order("music_sessions_user_history.user_id") - .where(%Q{ music_sessions_user_history.music_session_id = '#{music_session_id}'}) - end - - def duration_minutes - end_time = self.session_removed_at || Time.now - (end_time - self.created_at) / 60.0 - end - - def music_session_user_histories - @msuh ||= JamRuby::MusicSessionUserHistory - .where(:music_session_id => self.music_session_id) - .order('created_at DESC') - end - - def comments - @comments ||= JamRuby::MusicSessionComment - .where(:music_session_id => self.music_session_id) - .order('created_at DESC') - end - - def likes - @likes ||= JamRuby::MusicSessionLiker - .where(:music_session_id => self.music_session_id) - end - - def self.save(music_session) - session_history = MusicSessionHistory.find_by_music_session_id(music_session.id) - - if session_history.nil? - session_history = MusicSessionHistory.new - end - - session_history.music_session_id = music_session.id - session_history.description = music_session.description unless music_session.description.nil? - session_history.user_id = music_session.creator.id - session_history.band_id = music_session.band.id unless music_session.band.nil? - session_history.genres = music_session.genres.map { |g| g.id }.join SEPARATOR if music_session.genres.count > 0 - session_history.fan_access = music_session.fan_access - session_history.save! - end - - def is_over? - music_session.nil? || !session_removed_at.nil? - end - - def has_mount? - music_session && music_session.mount - end - - def recordings - Recording.where(music_session_id: self.id) - end - - def end_history - self.update_attribute(:session_removed_at, Time.now) - - - # ensure all user histories are closed - music_session_user_histories.each do |music_session_user_history| - music_session_user_history.end_history - - # then update any users that need their user progress updated - if music_session_user_history.duration_minutes > 15 && music_session_user_history.max_concurrent_connections >= 3 - music_session_user_history.user.update_progression_field(:first_real_music_session_at) - end - end - - end - - def self.removed_music_session(session_id) - hist = self - .where(:music_session_id => session_id) - .limit(1) - .first - - hist.end_history if hist - - Notification.send_session_ended(session_id) - end - - def remove_non_alpha_num(token) - token.gsub(/[^0-9A-Za-z]/, '') - end - - private - def generate_share_token - self.id = music_session.id # unify music_session.id and music_session_history.id - - token = loop do - token = SecureRandom.urlsafe_base64(SHARE_TOKEN_LENGTH, false) - token = remove_non_alpha_num(token) - token.upcase! - break token unless ShareToken.exists?(token: token) - end - - self.share_token = ShareToken.new - self.share_token.token = token - self.share_token.shareable_type = "session" - end - - end -end diff --git a/ruby/lib/jam_ruby/models/music_session_liker.rb b/ruby/lib/jam_ruby/models/music_session_liker.rb index b77aa8806..26df96fc4 100644 --- a/ruby/lib/jam_ruby/models/music_session_liker.rb +++ b/ruby/lib/jam_ruby/models/music_session_liker.rb @@ -5,7 +5,7 @@ module JamRuby self.primary_key = 'id' - belongs_to :music_session_history, class_name:"JamRuby::MusicSessionHistory", foreign_key: "music_session_id", :counter_cache => :like_count + belongs_to :music_session, class_name:"JamRuby::MusicSession", foreign_key: "music_session_id", :counter_cache => :like_count belongs_to :user, class_name: "JamRuby::User", foreign_key: "liker_id" diff --git a/ruby/lib/jam_ruby/models/music_session_perf_data.rb b/ruby/lib/jam_ruby/models/music_session_perf_data.rb index c06c197fe..accdcb681 100644 --- a/ruby/lib/jam_ruby/models/music_session_perf_data.rb +++ b/ruby/lib/jam_ruby/models/music_session_perf_data.rb @@ -7,13 +7,13 @@ module JamRuby attr_accessible :uri - belongs_to(:music_session_history, - :class_name => "JamRuby::MusicSessionHistory", + belongs_to(:music_session, + :class_name => "JamRuby::MusicSession", :foreign_key => :music_session_id) # mount_uploader :uri, PerfDataUploader - validates :music_session_history, :presence => true + validates :music_session, :presence => true validates :client_id, :presence => true validates :uri, :presence => true diff --git a/ruby/lib/jam_ruby/models/music_session_user_history.rb b/ruby/lib/jam_ruby/models/music_session_user_history.rb index 5939f8bff..f287604ae 100644 --- a/ruby/lib/jam_ruby/models/music_session_user_history.rb +++ b/ruby/lib/jam_ruby/models/music_session_user_history.rb @@ -6,21 +6,27 @@ module JamRuby self.primary_key = 'id' attr_accessible :max_concurrent_connections, :session_removed_at, :rating + validates_inclusion_of :rating, :in => -1..1, :allow_nil => true belongs_to(:user, :class_name => "JamRuby::User", :foreign_key => "user_id", :inverse_of => :music_session_user_histories) - belongs_to(:music_session_history, - :class_name => "MusicSessionHistory", + belongs_to(:music_session, + :class_name => "MusicSession", :foreign_key => "music_session_id") - validates_inclusion_of :rating, :in => 0..2, :allow_nil => true - after_save :track_user_progression + def self.latest_history(client_id) + self.where(:client_id => client_id) + .order('created_at DESC') + .limit(1) + .includes(:user) + .first + end - def music_session_history - @msh ||= JamRuby::MusicSessionHistory.find_by_music_session_id(self.music_session_id) + def music_session + @msh ||= JamRuby::MusicSession.find_by_music_session_id(self.music_session_id) end def perf_data @@ -104,10 +110,23 @@ module JamRuby self.perf_data.try(:uri) end - def track_user_progression - if self.rating == 0 - user.update_progression_field(:first_good_music_session_at) - end + def add_rating(rval, comment='') + rval = rval.to_i + self.rating = rval if 0 != rval + self.rating_comment = comment end + + MIN_SESSION_DURATION_RATING = 60 + + def should_rate_session? + (2 <= music_session.unique_users.all.count && + MIN_SESSION_DURATION_RATING < (Time.now - music_session.created_at).seconds) || + Rails.env.development? + end + + def good_rating? + 0 < self.rating.to_i + end + end end diff --git a/ruby/lib/jam_ruby/models/notification.rb b/ruby/lib/jam_ruby/models/notification.rb index 4318619d2..57f011b22 100644 --- a/ruby/lib/jam_ruby/models/notification.rb +++ b/ruby/lib/jam_ruby/models/notification.rb @@ -10,7 +10,7 @@ module JamRuby belongs_to :target_user, :class_name => "JamRuby::User", :foreign_key => "target_user_id" belongs_to :source_user, :class_name => "JamRuby::User", :foreign_key => "source_user_id" belongs_to :band, :class_name => "JamRuby::Band", :foreign_key => "band_id" - belongs_to :session, :class_name => "JamRuby::MusicSession", :foreign_key => "session_id" + belongs_to :session, :class_name => "JamRuby::ActiveMusicSession", :foreign_key => "session_id" belongs_to :recording, :class_name => "JamRuby::Recording", :foreign_key => "recording_id" validates :target_user, :presence => true diff --git a/ruby/lib/jam_ruby/models/promotional.rb b/ruby/lib/jam_ruby/models/promotional.rb index 109b8bccd..532fe0003 100644 --- a/ruby/lib/jam_ruby/models/promotional.rb +++ b/ruby/lib/jam_ruby/models/promotional.rb @@ -83,8 +83,8 @@ class JamRuby::PromoLatest < JamRuby::Promotional attr_accessible :latest - def music_session_history - self.latest if self.latest.is_a? MusicSessionHistory + def music_session + self.latest if self.latest.is_a? MusicSession end def recording @@ -101,7 +101,7 @@ class JamRuby::PromoLatest < JamRuby::Promotional def update_with_params(params) if (latest_id = params[:latest_id]).present? self.latest = Recording.where(:id => latest_id).limit(1).first || - MusicSessionHistory.where(:id => latest_id).limit(1).first + MusicSession.where(:id => latest_id).limit(1).first end self.position = params[:position] self.aasm_state = params[:aasm_state] diff --git a/ruby/lib/jam_ruby/models/recording.rb b/ruby/lib/jam_ruby/models/recording.rb index 58ef9d126..143aed8f6 100644 --- a/ruby/lib/jam_ruby/models/recording.rb +++ b/ruby/lib/jam_ruby/models/recording.rb @@ -16,7 +16,7 @@ module JamRuby belongs_to :owner, :class_name => "JamRuby::User", :inverse_of => :owned_recordings, :foreign_key => 'owner_id' belongs_to :band, :class_name => "JamRuby::Band", :inverse_of => :recordings - belongs_to :music_session, :class_name => "JamRuby::MusicSession", :inverse_of => :recordings + belongs_to :music_session, :class_name => "JamRuby::ActiveMusicSession", :inverse_of => :recordings, foreign_key: :music_session_id accepts_nested_attributes_for :recorded_tracks, :mixes, :claimed_recordings, allow_destroy: true diff --git a/ruby/lib/jam_ruby/models/search.rb b/ruby/lib/jam_ruby/models/search.rb index 4de3b0f4c..77509d8ad 100644 --- a/ruby/lib/jam_ruby/models/search.rb +++ b/ruby/lib/jam_ruby/models/search.rb @@ -109,7 +109,7 @@ module JamRuby F_SORT_OPTS = [F_SORT_RECENT, F_SORT_LENGTH, F_SORT_OLDEST] SHOW_BOTH = ['Sessions & Recordings', :all] - SHOW_SESSIONS = ['Sessions', :music_session_history] + SHOW_SESSIONS = ['Sessions', :music_session] SHOW_RECORDINGS = ['Recordings', :recording] SHOW_OPTS = [SHOW_BOTH, SHOW_SESSIONS, SHOW_RECORDINGS] @@ -300,8 +300,8 @@ module JamRuby sel_str = 'bands.*' case ordering = self.order_param(params) when :plays # FIXME: double counting? - sel_str = "COUNT(records)+COUNT(sessions) AS play_count, #{sel_str}" - rel = rel.joins("LEFT JOIN music_sessions AS sessions ON sessions.band_id = bands.id") + 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") .joins("LEFT JOIN recordings AS records ON records.band_id = bands.id") .group("bands.id") .order("play_count DESC, bands.created_at DESC") @@ -311,7 +311,7 @@ module JamRuby .group("bands.id") .order("COUNT(follows) DESC, bands.created_at DESC") when :playing - rel = rel.joins("LEFT JOIN music_sessions_history AS msh ON msh.band_id = bands.id") + rel = rel.joins("LEFT JOIN music_sessions AS msh ON msh.band_id = bands.id") .where('msh.music_session_id IS NOT NULL AND msh.session_removed_at IS NULL') .order("bands.created_at DESC") end diff --git a/ruby/lib/jam_ruby/models/track.rb b/ruby/lib/jam_ruby/models/track.rb index 6ad15159a..e827914f9 100644 --- a/ruby/lib/jam_ruby/models/track.rb +++ b/ruby/lib/jam_ruby/models/track.rb @@ -151,7 +151,7 @@ module JamRuby track.client_track_id = client_track_id end - track.updated_at = Time.now.getutc + track.updated_at = Time.now track.save return track end diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index 22a4a426e..17b9910f1 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -17,7 +17,7 @@ module JamRuby attr_accessible :first_name, :last_name, :email, :city, :password, :password_confirmation, :state, :country, :birth_date, :subscribe_email, :terms_of_service, :original_fpfile, :cropped_fpfile, :cropped_large_fpfile, :cropped_s3_path, :cropped_large_s3_path, :photo_url, :large_photo_url, :crop_selection, :lat, :lng # updating_password corresponds to a lost_password - attr_accessor :updating_password, :updating_email, :updated_email, :update_email_confirmation_url, :administratively_created, :current_password, :setting_password, :confirm_current_password, :updating_avatar, :updating_progression_field + attr_accessor :updating_password, :updating_email, :updated_email, :update_email_confirmation_url, :administratively_created, :current_password, :setting_password, :confirm_current_password, :updating_avatar, :updating_progression_field, :mods_json belongs_to :icecast_server_group, class_name: "JamRuby::IcecastServerGroup", inverse_of: :users, foreign_key: 'icecast_server_group_id' @@ -43,7 +43,7 @@ module JamRuby has_many :owned_recordings, :class_name => "JamRuby::Recording", :foreign_key => "owner_id" has_many :recordings, :through => :claimed_recordings, :class_name => "JamRuby::Recording" has_many :claimed_recordings, :class_name => "JamRuby::ClaimedRecording", :inverse_of => :user - has_many :playing_claimed_recordings, :class_name => "JamRuby::MusicSession", :inverse_of => :claimed_recording_initiator + has_many :playing_claimed_recordings, :class_name => "JamRuby::ActiveMusicSession", :inverse_of => :claimed_recording_initiator # self.id = user_id in likes table has_many :likings, :class_name => "JamRuby::Like", :inverse_of => :user, :dependent => :destroy @@ -71,8 +71,8 @@ module JamRuby has_many :inverse_friends, :through => :inverse_friendships, :source => :user, :class_name => "JamRuby::User" # connections / music sessions - has_many :created_music_sessions, :foreign_key => "user_id", :inverse_of => :user, :class_name => "JamRuby::MusicSession" # sessions *created* by the user - has_many :music_sessions, :through => :connections, :class_name => "JamRuby::MusicSession" + has_many :created_music_sessions, :foreign_key => "user_id", :inverse_of => :user, :class_name => "JamRuby::ActiveMusicSession" # sessions *created* by the user + has_many :music_sessions, :through => :connections, :class_name => "JamRuby::ActiveMusicSession" # invitations has_many :received_invitations, :foreign_key => "receiver_id", :inverse_of => :receiver, :class_name => "JamRuby::Invitation" @@ -87,7 +87,7 @@ module JamRuby has_many :sent_band_invitations, :inverse_of => :sender, :foreign_key => "creator_id", :class_name => "JamRuby::BandInvitation" # session history - has_many :music_session_histories, :foreign_key => "user_id", :class_name => "JamRuby::MusicSessionHistory", :inverse_of => :user + has_many :music_session_histories, :foreign_key => "user_id", :class_name => "JamRuby::MusicSession", :inverse_of => :user has_many :music_session_user_histories, :foreign_key => "user_id", :class_name => "JamRuby::MusicSessionUserHistory", :inverse_of => :user # saved tracks @@ -105,6 +105,8 @@ module JamRuby # affiliate_partner has_one :affiliate_partner, :class_name => "JamRuby::AffiliatePartner", :foreign_key => :partner_user_id belongs_to :affiliate_referral, :class_name => "JamRuby::AffiliatePartner", :foreign_key => :affiliate_referral_id, :counter_cache => :referral_user_count + # diagnostics + has_many :diagnostics, :class_name => "JamRuby::Diagnostic" # This causes the authenticate method to be generated (among other stuff) #has_secure_password @@ -126,6 +128,7 @@ module JamRuby validates :subscribe_email, :inclusion => {:in => [nil, true, false]} validates :musician, :inclusion => {:in => [true, false]} validates :show_whats_next, :inclusion => {:in => [nil, true, false]} + validates :mods, json: true # custom validators validate :validate_musician_instruments @@ -287,12 +290,25 @@ module JamRuby self.music_sessions.size end + # mods comes back as text; so give ourselves a parsed version + def mods_json + @mods_json ||= mods ? JSON.parse(mods, symbolize_names: true) : {} + end + + def heartbeat_interval_client + mods_json[:heartbeat_interval_client] + end + + def connection_expire_time_client + mods_json[:connection_expire_time_client] + end + def recent_history recordings = Recording.where(:owner_id => self.id) .order('created_at DESC') .limit(10) - msh = MusicSessionHistory.where(:user_id => self.id) + msh = MusicSession.where(:user_id => self.id) .order('created_at DESC') .limit(10) @@ -339,7 +355,7 @@ module JamRuby end def session_history(user_id, band_id = nil, genre = nil) - return MusicSessionHistory.index(self, user_id, band_id, genre) + return MusicSession.index(self, user_id, band_id, genre) end def session_user_history(user_id, session_id) @@ -363,7 +379,7 @@ module JamRuby return first_name + ' ' + last_name end - return id + id end def set_password(old_password, new_password, new_password_confirmation) @@ -551,7 +567,7 @@ module JamRuby self.biography = biography end - self.updated_at = Time.now.getutc + self.updated_at = Time.now self.save end @@ -640,7 +656,7 @@ module JamRuby end # def create_session_like(targetSessionId) - # targetSession = MusicSessionHistory.find(targetSessionId) + # targetSession = MusicSession.find(targetSessionId) # like = Like.new # like.likable = targetSession @@ -688,15 +704,7 @@ module JamRuby unless user.nil? # only save genre id and description - genres = [] - unless music_session.genres.nil? - music_session.genres.each do |genre| - g = Hash.new - g["id"] = genre.id - g["description"] = genre.description - genres << g - end - end + genres = [{id: music_session.genre.id, description: music_session.genre.description}] # only save invitation receiver id and name invitees = [] @@ -709,7 +717,7 @@ module JamRuby end end - session_settings = { :band_id => music_session.band_id, + session_settings = { :band_id => music_session.band_id, :musician_access => music_session.musician_access, :approval_required => music_session.approval_required, :fan_chat => music_session.fan_chat, diff --git a/ruby/lib/jam_ruby/resque/google_analytics_event.rb b/ruby/lib/jam_ruby/resque/google_analytics_event.rb index 58c393084..55da4b358 100644 --- a/ruby/lib/jam_ruby/resque/google_analytics_event.rb +++ b/ruby/lib/jam_ruby/resque/google_analytics_event.rb @@ -25,7 +25,7 @@ module JamRuby def self.perform(args={}) session_id, interval_idx = args['session_id'], args['interval_idx'].to_i - return unless session_id && session = MusicSession.find(session_id) + return unless session_id && session = MusicSession.find_by_id(session_id) GoogleAnalyticsEvent.enqueue(CAT_SESS_DUR, ACTION_SESS_DUR, SESSION_INTERVALS[interval_idx]) interval_idx += 1 @@ -47,7 +47,7 @@ module JamRuby @queue = QUEUE_BAND_TRACKER def self.perform(session_id) - return unless session = MusicSession.find(session_id) + return unless session = ActiveMusicSession.find(session_id) band = session.band if band.in_real_session?(session) band.update_attribute(:did_real_session, true) diff --git a/ruby/spec/factories.rb b/ruby/spec/factories.rb index 30355116d..e914a2d66 100644 --- a/ruby/spec/factories.rb +++ b/ruby/spec/factories.rb @@ -31,46 +31,66 @@ FactoryGirl.define do factory :single_user_session do after(:create) do |user, evaluator| - music_session = FactoryGirl.create(:music_session, :creator => user) - connection = FactoryGirl.create(:connection, :user => user, :music_session => music_session) + active_music_session = FactoryGirl.create(:active_music_session, :creator => user) + connection = FactoryGirl.create(:connection, :user => user, :music_session => active_music_session) end end end - factory :music_session_no_history, :class => JamRuby::MusicSession do - sequence(:description) { |n| "Music Session #{n}" } + factory :musician_instrument, :class => JamRuby::MusicianInstrument do + instrument { JamRuby::Instrument.find('electric guitar') } + proficiency_level 1 + priority 0 + end + + + factory :active_music_session_no_user_history, :class => JamRuby::ActiveMusicSession do + + association :creator, factory: :user + + ignore do + name "My Music Session" + description "Come Music Session" + fan_chat true + fan_access true + approval_required false + musician_access true + legal_terms true + genre JamRuby::Genre.first + band nil + end + + + before(:create) do |session, evaluator| + music_session = FactoryGirl.create(:music_session, name: evaluator.name, description: evaluator.description, fan_chat: evaluator.fan_chat, + fan_access: evaluator.fan_access, approval_required: evaluator.approval_required, musician_access: evaluator.musician_access, + genre: evaluator.genre, creator: evaluator.creator, band: evaluator.band) + session.id = music_session.id + end + + factory :active_music_session do + after(:create) { |session| + FactoryGirl.create(:music_session_user_history, :history => session.music_session, :user => session.creator) + } + + factory :active_music_session_with_mount do + association :mount, :factory => :icecast_mount + end + end + end + + factory :music_session, :class => JamRuby::MusicSession do + sequence(:name) { |n| "Music Session #{n}" } + sequence(:description) { |n| "Music Session Description #{n}" } fan_chat true fan_access true approval_required false musician_access true legal_terms true - genres [JamRuby::Genre.first] + language 'english' + legal_policy 'standard' + genre JamRuby::Genre.first association :creator, :factory => :user - - factory :music_session do - - after(:create) { |session| - FactoryGirl.create(:music_session_user_history, :history => session.music_session_history, :user => session.creator) - } - - factory :music_session_with_mount do - association :mount, :factory => :icecast_mount - end - - end - - end - - factory :music_session_history, :class => JamRuby::MusicSessionHistory do - ignore do - music_session nil - end - - fan_access true - music_session_id { music_session.id } - description { music_session.description } - user_id { music_session.user_id } - band_id { music_session.band_id } end factory :music_session_user_history, :class => JamRuby::MusicSessionUserHistory do @@ -80,7 +100,7 @@ FactoryGirl.define do end instruments 'guitar' - music_session_id { history.music_session_id } + music_session_id { history.id } user_id { user.id } sequence(:client_id) { |n| "Connection #{n}" } end @@ -154,7 +174,7 @@ FactoryGirl.define do factory :recording, :class => JamRuby::Recording do association :owner, factory: :user - association :music_session, factory: :music_session + association :music_session, factory: :active_music_session association :band, factory: :band factory :recording_with_track do @@ -196,12 +216,6 @@ FactoryGirl.define do } end - factory :musician_instrument, :class => JamRuby::MusicianInstrument do - instrument { Instrument.find('electric guitar') } - proficiency_level 1 - priority 0 - end - factory :invited_user, :class => JamRuby::InvitedUser do sequence(:email) { |n| "user#{n}@someservice.com" } autofriend false @@ -305,7 +319,7 @@ FactoryGirl.define do association :mount_template, :factory => :icecast_mount_template factory :iceast_mount_with_music_session do - association :music_session, :factory => :music_session + association :music_session, :factory => :active_music_session end end end @@ -438,4 +452,10 @@ FactoryGirl.define do message Faker::Lorem.characters(10) end end + + factory :diagnostic, :class => JamRuby::Diagnostic do + type JamRuby::Diagnostic::NO_HEARTBEAT_ACK + creator JamRuby::Diagnostic::CLIENT + data Faker::Lorem.sentence + end end diff --git a/ruby/spec/jam_ruby/connection_manager_spec.rb b/ruby/spec/jam_ruby/connection_manager_spec.rb index 25c659529..1e74c6369 100644 --- a/ruby/spec/jam_ruby/connection_manager_spec.rb +++ b/ruby/spec/jam_ruby/connection_manager_spec.rb @@ -4,6 +4,10 @@ require 'spec_helper' describe ConnectionManager do TRACKS = [{"instrument_id" => "electric guitar", "sound" => "mono", "client_track_id" => "some_client_track_id"}] + STALE_TIME = 40 + EXPIRE_TIME = 60 + STALE_BUT_NOT_EXPIRED = 50 + DEFINITELY_EXPIRED = 70 before do @conn = PG::Connection.new(:dbname => SpecDb::TEST_DB_NAME, :user => "postgres", :password => "postgres", :host => "localhost") @@ -17,17 +21,6 @@ describe ConnectionManager do end end - def create_music_session(user_id, options={}) - default_options = {:musician_access => true, :fan_chat => true, :fan_access => true, :approval_required=> false} - options = default_options.merge(options) - description = "some session" - @conn.exec("INSERT INTO music_sessions (user_id, description, musician_access, approval_required, fan_chat, fan_access) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id", [user_id, description, options[:musician_access], options[:approval_required], options[:fan_chat], options[:fan_access]]) do |result| - session_id = result.getvalue(0, 0) - @conn.exec("INSERT INTO music_sessions_history (music_session_id, description, user_id, fan_access) VALUES ($1, $2, $3, $4)", [session_id, description, user_id, true]) - return session_id - end - end - def assert_num_connections(client_id, expected_num_connections) # make sure the connection is still there @conn.exec("SELECT count(*) FROM connections where client_id = $1", [client_id]) do |result| @@ -36,7 +29,7 @@ describe ConnectionManager do end def assert_session_exists(music_session_id, exists) - @conn.exec("SELECT count(*) FROM music_sessions where id = $1", [music_session_id]) do |result| + @conn.exec("SELECT count(*) FROM active_music_sessions where id = $1", [music_session_id]) do |result| if exists result.getvalue(0, 0).should == "1" else @@ -53,8 +46,8 @@ describe ConnectionManager do user.save! user = nil - @connman.create_connection(user_id, client_id, "1.1.1.1", 'client') - expect { @connman.create_connection(user_id, client_id, "1.1.1.1", 'client') }.to raise_error(PG::Error) + @connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME) + expect { @connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME) }.to raise_error(PG::Error) end it "create connection then delete it" do @@ -63,7 +56,7 @@ describe ConnectionManager do #user_id = create_user("test", "user2", "user2@jamkazam.com") user = FactoryGirl.create(:user) - count = @connman.create_connection(user.id, client_id, "1.1.1.1", 'client') + count = @connman.create_connection(user.id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME) count.should == 1 @@ -93,7 +86,7 @@ describe ConnectionManager do #user_id = create_user("test", "user2", "user2@jamkazam.com") user = FactoryGirl.create(:user) - count = @connman.create_connection(user.id, client_id, "1.1.1.1", 'client') + count = @connman.create_connection(user.id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME) count.should == 1 @@ -109,7 +102,7 @@ describe ConnectionManager do cc.addr.should == 0x01010101 cc.locidispid.should == 17192000002 - @connman.reconnect(cc, nil, "33.1.2.3") + @connman.reconnect(cc, nil, "33.1.2.3", STALE_TIME, EXPIRE_TIME) cc = Connection.find_by_client_id!(client_id) cc.connected?.should be_true @@ -222,20 +215,21 @@ describe ConnectionManager do it "flag stale connection" do client_id = "client_id8" user_id = create_user("test", "user8", "user8@jamkazam.com") - @connman.create_connection(user_id, client_id, "1.1.1.1", 'client') + @connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME) num = JamRuby::Connection.count(:conditions => ['aasm_state = ?','connected']) num.should == 1 assert_num_connections(client_id, num) - @connman.flag_stale_connections(60) + @connman.flag_stale_connections() assert_num_connections(client_id, num) - sleep(1) + conn = Connection.find_by_client_id(client_id) + set_updated_at(conn, Time.now - STALE_BUT_NOT_EXPIRED) num = JamRuby::Connection.count(:conditions => ["updated_at < (NOW() - interval '#{1} second') AND aasm_state = 'connected'"]) num.should == 1 # this should change the aasm_state to stale - @connman.flag_stale_connections(1) + @connman.flag_stale_connections() num = JamRuby::Connection.count(:conditions => ["updated_at < (NOW() - interval '#{1} second') AND aasm_state = 'connected'"]) num.should == 0 @@ -244,31 +238,39 @@ describe ConnectionManager do num.should == 1 assert_num_connections(client_id, 1) - cids = @connman.stale_connection_client_ids(1) - cids.size.should == 1 - cids[0].should == client_id - cids.each { |cid| @connman.delete_connection(cid) } + conn = Connection.find_by_client_id(client_id) + set_updated_at(conn, Time.now - DEFINITELY_EXPIRED) + + cids = @connman.stale_connection_client_ids() + cids.size.should == 1 + cids[0][:client_id].should == client_id + cids[0][:client_type].should == Connection::TYPE_CLIENT + cids[0][:music_session_id].should be_nil + cids[0][:user_id].should == user_id + + cids.each { |cid| @connman.delete_connection(cid[:client_id]) } - sleep(1) assert_num_connections(client_id, 0) end it "expires stale connection" do client_id = "client_id8" user_id = create_user("test", "user8", "user8@jamkazam.com") - @connman.create_connection(user_id, client_id, "1.1.1.1", 'client') + @connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME) - sleep(1) - @connman.flag_stale_connections(1) + conn = Connection.find_by_client_id(client_id) + set_updated_at(conn, Time.now - STALE_BUT_NOT_EXPIRED) + + @connman.flag_stale_connections assert_num_connections(client_id, 1) # assert_num_connections(client_id, JamRuby::Connection.count(:conditions => ['aasm_state = ?','stale'])) - @connman.expire_stale_connections(60) + @connman.expire_stale_connections assert_num_connections(client_id, 1) - sleep(1) + set_updated_at(conn, Time.now - DEFINITELY_EXPIRED) # this should delete the stale connection - @connman.expire_stale_connections(1) + @connman.expire_stale_connections assert_num_connections(client_id, 0) end @@ -276,12 +278,11 @@ describe ConnectionManager do client_id = "client_id9" user_id = create_user("test", "user9", "user9@jamkazam.com") - music_session_id = create_music_session(user_id) - + music_session = FactoryGirl.create(:active_music_session, user_id: user_id) + music_session_id = music_session.id user = User.find(user_id) - music_session = MusicSession.find(music_session_id) - @connman.create_connection(user_id, client_id, "1.1.1.1", 'client') + @connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME) connection = @connman.join_music_session(user, client_id, music_session, true, TRACKS) connection.errors.any?.should be_false @@ -302,10 +303,10 @@ describe ConnectionManager do client_id = "client_id10" user_id = create_user("test", "user10", "user10@jamkazam.com") - music_session_id = create_music_session(user_id) + music_session = FactoryGirl.create(:active_music_session, user_id: user_id) + music_session_id = music_session.id user = User.find(user_id) - music_session = MusicSession.find(music_session_id) expect { @connman.join_music_session(user, client_id, music_session, true, TRACKS) }.to raise_error(ActiveRecord::RecordNotFound) @@ -317,13 +318,12 @@ describe ConnectionManager do client_id2 = "client_id10.12" user_id = create_user("test", "user10.11", "user10.11@jamkazam.com", :musician => true) user_id2 = create_user("test", "user10.12", "user10.12@jamkazam.com", :musician => false) - @connman.create_connection(user_id, client_id, "1.1.1.1", 'client') - @connman.create_connection(user_id2, client_id2, "1.1.1.1", 'client') - - music_session_id = create_music_session(user_id) + @connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME) + @connman.create_connection(user_id2, client_id2, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME) + music_session = FactoryGirl.create(:active_music_session, user_id: user_id) + music_session_id = music_session.id user = User.find(user_id) - music_session = MusicSession.find(music_session_id) @connman.join_music_session(user, client_id, music_session, true, TRACKS) @@ -336,13 +336,12 @@ describe ConnectionManager do it "as_musician is coerced to boolean" do client_id = "client_id10.2" - user_id = create_user("test", "user10.2", "user10.2@jamkazam.com", :musician => false) - @connman.create_connection(user_id, client_id, "1.1.1.1", 'client') - music_session_id = create_music_session(user_id) + user_id = create_user("test", "user10.2", "user10.2@jamkazam.com") + @connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME) + music_session = FactoryGirl.create(:active_music_session, user_id: user_id) user = User.find(user_id) - music_session = MusicSession.find(music_session_id) connection = @connman.join_music_session(user, client_id, music_session, 'blarg', TRACKS) connection.errors.size.should == 0 @@ -355,13 +354,13 @@ describe ConnectionManager do fan_client_id = "client_id10.4" musician_id = create_user("test", "user10.3", "user10.3@jamkazam.com") fan_id = create_user("test", "user10.4", "user10.4@jamkazam.com", :musician => false) - @connman.create_connection(musician_id, musician_client_id, "1.1.1.1", 'client') - @connman.create_connection(fan_id, fan_client_id, "1.1.1.1", 'client') + @connman.create_connection(musician_id, musician_client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME) + @connman.create_connection(fan_id, fan_client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME) - music_session_id = create_music_session(musician_id, :fan_access => false) + music_session = FactoryGirl.create(:active_music_session, :fan_access => false, user_id: musician_id) + music_session_id = music_session.id user = User.find(musician_id) - music_session = MusicSession.find(music_session_id) @connman.join_music_session(user, musician_client_id, music_session, true, TRACKS) @@ -376,12 +375,11 @@ describe ConnectionManager do client_id = "client_id20" user_id = create_user("test", "user20", "user20@jamkazam.com") user_id2 = create_user("test", "user21", "user21@jamkazam.com") - music_session_id = create_music_session(user_id) - + music_session = FactoryGirl.create(:active_music_session, user_id: user_id) + music_session_id = music_session.id user = User.find(user_id2) - music_session = MusicSession.find(music_session_id) - @connman.create_connection(user_id, client_id, "1.1.1.1", 'client') + @connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME) # specify real user id, but not associated with this session expect { @connman.join_music_session(user, client_id, music_session, true, TRACKS) } .to raise_error(ActiveRecord::RecordNotFound) end @@ -391,9 +389,9 @@ describe ConnectionManager do user_id = create_user("test", "user11", "user11@jamkazam.com") user = User.find(user_id) - music_session = MusicSession.new + music_session = ActiveMusicSession.new - @connman.create_connection(user_id, client_id, "1.1.1.1", 'client') + @connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME) connection = @connman.join_music_session(user, client_id, music_session, true, TRACKS) connection.errors.size.should == 1 connection.errors.get(:music_session).should == [ValidationMessages::MUSIC_SESSION_MUST_BE_SPECIFIED] @@ -403,12 +401,11 @@ describe ConnectionManager do client_id = "client_id11.1" user_id = create_user("test", "user11.1", "user11.1@jamkazam.com") user_id2 = create_user("test", "user11.2", "user11.2@jamkazam.com") - music_session_id = create_music_session(user_id, :approval_required => true) - + music_session = FactoryGirl.create(:active_music_session, :approval_required => true, user_id: user_id) + music_session_id = music_session.id user = User.find(user_id2) - music_session = MusicSession.find(music_session_id) - @connman.create_connection(user_id, client_id, "1.1.1.1", 'client') + @connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME) # specify real user id, but not associated with this session expect { @connman.join_music_session(user, client_id, music_session, true, TRACKS) } .to raise_error(ActiveRecord::RecordNotFound) end @@ -420,9 +417,9 @@ describe ConnectionManager do user_id = create_user("test", "user12", "user12@jamkazam.com") user = User.find(user_id) - dummy_music_session = MusicSession.new + dummy_music_session = ActiveMusicSession.new - @connman.create_connection(user_id, client_id, "1.1.1.1", 'client') + @connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME) expect { @connman.leave_music_session(user, Connection.find_by_client_id(client_id), dummy_music_session) }.to raise_error(JamRuby::StateError) end @@ -431,14 +428,13 @@ describe ConnectionManager do client_id = "client_id13" user_id = create_user("test", "user13", "user13@jamkazam.com") - music_session_id = create_music_session(user_id) - + music_session = FactoryGirl.create(:active_music_session, user_id: user_id) + music_session_id = music_session.id user = User.find(user_id) - music_session = MusicSession.find(music_session_id) - dummy_music_session = MusicSession.new + dummy_music_session = ActiveMusicSession.new - @connman.create_connection(user_id, client_id, "1.1.1.1", 'client') + @connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME) @connman.join_music_session(user, client_id, music_session, true, TRACKS) expect { @connman.leave_music_session(user, Connection.find_by_client_id(client_id), dummy_music_session) }.to raise_error(JamRuby::StateError) end @@ -447,12 +443,11 @@ describe ConnectionManager do client_id = "client_id14" user_id = create_user("test", "user14", "user14@jamkazam.com") - music_session_id = create_music_session(user_id) - + music_session = FactoryGirl.create(:active_music_session, user_id: user_id) + music_session_id = music_session.id user = User.find(user_id) - music_session = MusicSession.find(music_session_id) - @connman.create_connection(user_id, client_id, "1.1.1.1", 'client') + @connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME) @connman.join_music_session(user, client_id, music_session, true, TRACKS) assert_session_exists(music_session_id, true) @@ -490,19 +485,17 @@ describe ConnectionManager do # and a connection can only point to one active music_session at a time. this is a test of # the latter but we need a test of the former, too. - user_id = create_user("test", "user11", "user11@jamkazam.com") user = User.find(user_id) client_id1 = Faker::Number.number(20) - @connman.create_connection(user_id, client_id1, "1.1.1.1", 'client') - music_session1 = MusicSession.find(create_music_session(user_id)) + @connman.create_connection(user_id, client_id1, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME) + music_session1 = FactoryGirl.create(:active_music_session, :user_id => user_id) connection1 = @connman.join_music_session(user, client_id1, music_session1, true, TRACKS) - connection1.errors.size.should == 0 - music_session2 = MusicSession.find(create_music_session(user_id)) + music_session2 = FactoryGirl.create(:active_music_session, :user_id => user_id) connection2 = @connman.join_music_session(user, client_id1, music_session2, true, TRACKS) connection2.errors.size.should == 1 diff --git a/ruby/spec/jam_ruby/models/band_filter_search_spec.rb b/ruby/spec/jam_ruby/models/band_filter_search_spec.rb index d0553d951..4b8f69c1b 100644 --- a/ruby/spec/jam_ruby/models/band_filter_search_spec.rb +++ b/ruby/spec/jam_ruby/models/band_filter_search_spec.rb @@ -105,7 +105,7 @@ describe 'Band search' do def make_session(band) usr = band.users[0] - session = FactoryGirl.create(:music_session, :creator => usr, :description => "Session", :band => band) + session = FactoryGirl.create(:active_music_session, :creator => usr, :description => "Session", :band => band) FactoryGirl.create(:connection, :user => usr, :music_session => session) user = FactoryGirl.create(:user) session @@ -159,7 +159,7 @@ describe 'Band search' do it "by now playing" do # should get 1 result with 1 active session session = make_session(@band3) - #FactoryGirl.create(:music_session_history, :music_session => session) + #FactoryGirl.create(:active_music_session, :music_session => session) results = Search.band_filter({ :orderby => 'playing' }) expect(results.results.count).to be 1 @@ -168,7 +168,7 @@ describe 'Band search' do # should get 2 results with 2 active sessions # sort order should be created_at DESC session = make_session(@band4) - #FactoryGirl.create(:music_session_history, :music_session => session) + #FactoryGirl.create(:active_music_session, :music_session => session) results = Search.band_filter({ :orderby => 'playing' }) expect(results.results.count).to be 2 expect(results.results[0].id).to eq(@band4.id) diff --git a/ruby/spec/jam_ruby/models/claimed_recording_spec.rb b/ruby/spec/jam_ruby/models/claimed_recording_spec.rb index fae09a6ab..171749999 100644 --- a/ruby/spec/jam_ruby/models/claimed_recording_spec.rb +++ b/ruby/spec/jam_ruby/models/claimed_recording_spec.rb @@ -18,7 +18,7 @@ describe ClaimedRecording do @connection = FactoryGirl.create(:connection, :user => @user) @instrument = FactoryGirl.create(:instrument, :description => 'a great instrument') @track = FactoryGirl.create(:track, :connection => @connection, :instrument => @instrument) - @music_session = FactoryGirl.create(:music_session, :creator => @user, :musician_access => true) + @music_session = FactoryGirl.create(:active_music_session, :creator => @user, :musician_access => true) # @music_session.connections << @connection @music_session.save @connection.join_the_session(@music_session, true, nil) diff --git a/ruby/spec/jam_ruby/models/connection_spec.rb b/ruby/spec/jam_ruby/models/connection_spec.rb index 6fdaebf46..377bbdb1d 100644 --- a/ruby/spec/jam_ruby/models/connection_spec.rb +++ b/ruby/spec/jam_ruby/models/connection_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe JamRuby::Connection do let(:user) { FactoryGirl.create(:user) } - let (:music_session) { FactoryGirl.create(:music_session, :creator => user) } + let (:music_session) { FactoryGirl.create(:active_music_session, :creator => user) } it 'starts in the correct state' do connection = FactoryGirl.create(:connection, @@ -37,7 +37,7 @@ describe JamRuby::Connection do pending 'distance search changes' uu = FactoryGirl.create(:user) uu.lat.should == nil - msess = FactoryGirl.create(:music_session, :creator => uu) + msess = FactoryGirl.create(:active_music_session, :creator => uu) geocode = FactoryGirl.create(:geocoder) connection = FactoryGirl.create(:connection, :user => uu, diff --git a/ruby/spec/jam_ruby/models/diagnostic_spec.rb b/ruby/spec/jam_ruby/models/diagnostic_spec.rb new file mode 100644 index 000000000..8900deb21 --- /dev/null +++ b/ruby/spec/jam_ruby/models/diagnostic_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +describe Diagnostic do + let (:user) { FactoryGirl.create(:user) } + let (:diagnostic) { FactoryGirl.create(:diagnostic, user: user) } + + it 'can be made' do + diagnostic.save! + end + + it "validates type" do + diagnostic = FactoryGirl.build(:diagnostic, user: user, type: 'bleh') + + diagnostic.errors[:type].should == [] + + end + +end diff --git a/ruby/spec/jam_ruby/models/feed_spec.rb b/ruby/spec/jam_ruby/models/feed_spec.rb index 750cbd2f4..d7ba3391d 100644 --- a/ruby/spec/jam_ruby/models/feed_spec.rb +++ b/ruby/spec/jam_ruby/models/feed_spec.rb @@ -16,7 +16,7 @@ describe Feed do it "one claimed recording" do claimed_recording = FactoryGirl.create(:claimed_recording) MusicSessionUserHistory.delete_all # the factory makes a music_session while making the recording/claimed_recording - MusicSessionHistory.delete_all # the factory makes a music_session while making the recording/claimed_recording + MusicSession.delete_all # the factory makes a music_session while making the recording/claimed_recording feeds, start = Feed.index(user1) feeds.length.should == 1 feeds[0].recording == claimed_recording.recording @@ -28,7 +28,7 @@ describe Feed do recording.recorded_tracks << second_track FactoryGirl.create(:claimed_recording, recording: recording, user: second_track.user) MusicSessionUserHistory.delete_all # the factory makes a music_session while making the recording/claimed_recording - MusicSessionHistory.delete_all + MusicSession.delete_all # verify the mess above only made one recording Recording.count.should == 1 @@ -38,16 +38,16 @@ describe Feed do end it "one music session" do - music_session = FactoryGirl.create(:music_session) + music_session = FactoryGirl.create(:active_music_session) feeds, start = Feed.index(user1) feeds.length.should == 1 - feeds[0].music_session_history == music_session.music_session_history + feeds[0].music_session == music_session.music_session end it "does not return a recording with no claimed recordings" do recording = FactoryGirl.create(:recording) MusicSessionUserHistory.delete_all # the factory makes a music_session while making the recording/claimed_recording - MusicSessionHistory.delete_all + MusicSession.delete_all feeds, start = Feed.index(user1) feeds.length.should == 0 @@ -60,7 +60,7 @@ describe Feed do feeds, start = Feed.index(user1) feeds.length.should == 2 feeds[0].recording.should == claimed_recording.recording - feeds[1].music_session_history.should == claimed_recording.recording.music_session.music_session_history + feeds[1].music_session.should == claimed_recording.recording.music_session.music_session end it "sort by plays DESC" do @@ -80,14 +80,14 @@ describe Feed do feeds[0].recording.should == claimed_recording2.recording feeds[1].recording.should == claimed_recording1.recording - FactoryGirl.create(:playable_play, playable: claimed_recording1.recording.music_session.music_session_history, user: user1) - FactoryGirl.create(:playable_play, playable: claimed_recording1.recording.music_session.music_session_history, user: user2) - FactoryGirl.create(:playable_play, playable: claimed_recording1.recording.music_session.music_session_history, user: user3) + FactoryGirl.create(:playable_play, playable: claimed_recording1.recording.music_session.music_session, user: user1) + FactoryGirl.create(:playable_play, playable: claimed_recording1.recording.music_session.music_session, user: user2) + FactoryGirl.create(:playable_play, playable: claimed_recording1.recording.music_session.music_session, user: user3) feeds, start = Feed.index(user1, :sort => 'plays') feeds.length.should == 4 - feeds[0].music_session_history.should == claimed_recording1.recording.music_session.music_session_history + feeds[0].music_session.should == claimed_recording1.recording.music_session.music_session feeds[1].recording.should == claimed_recording2.recording feeds[2].recording.should == claimed_recording1.recording end @@ -109,13 +109,13 @@ describe Feed do feeds[0].recording.should == claimed_recording2.recording feeds[1].recording.should == claimed_recording1.recording - FactoryGirl.create(:music_session_like, music_session_history: claimed_recording1.recording.music_session.music_session_history, user: user1) - FactoryGirl.create(:music_session_like, music_session_history: claimed_recording1.recording.music_session.music_session_history, user: user2) - FactoryGirl.create(:music_session_like, music_session_history: claimed_recording1.recording.music_session.music_session_history, user: user3) + FactoryGirl.create(:music_session_like, music_session: claimed_recording1.recording.music_session.music_session, user: user1) + FactoryGirl.create(:music_session_like, music_session: claimed_recording1.recording.music_session.music_session, user: user2) + FactoryGirl.create(:music_session_like, music_session: claimed_recording1.recording.music_session.music_session, user: user3) feeds, start = Feed.index(user1, :sort => 'likes') feeds.length.should == 4 - feeds[0].music_session_history.should == claimed_recording1.recording.music_session.music_session_history + feeds[0].music_session.should == claimed_recording1.recording.music_session.music_session feeds[1].recording.should == claimed_recording2.recording feeds[2].recording.should == claimed_recording1.recording end @@ -126,18 +126,18 @@ describe Feed do # creates both recording and history record in feed claimed_recording1 = FactoryGirl.create(:claimed_recording) - feeds, start = Feed.index(user1, :type => 'music_session_history') + feeds, start = Feed.index(user1, :type => 'music_session') feeds.length.should == 1 - feeds[0].music_session_history == claimed_recording1.recording.music_session.music_session_history + feeds[0].music_session == claimed_recording1.recording.music_session.music_session end it "returns only sessions" do # creates both recording and history record in feed claimed_recording1 = FactoryGirl.create(:claimed_recording) - feeds, start = Feed.index(user1, :type => 'music_session_history') + feeds, start = Feed.index(user1, :type => 'music_session') feeds.length.should == 1 - feeds[0].music_session_history == claimed_recording1.recording.music_session.music_session_history + feeds[0].music_session == claimed_recording1.recording.music_session.music_session end end @@ -203,7 +203,7 @@ describe Feed do options[:start] = start feeds, start = Feed.index(user1, options) feeds.length.should == 1 - feeds[0].music_session_history.should == claimed_recording.recording.music_session.music_session_history + feeds[0].music_session.should == claimed_recording.recording.music_session.music_session options[:start] = start feeds, start = Feed.index(user1, options) @@ -214,12 +214,12 @@ describe Feed do it "supports likes pagination" do claimed_recording1 = FactoryGirl.create(:claimed_recording) - FactoryGirl.create(:music_session_like, music_session_history: claimed_recording1.recording.music_session.music_session_history, user: user1) + FactoryGirl.create(:music_session_like, music_session: claimed_recording1.recording.music_session.music_session, user: user1) options = {limit: 1, sort: 'likes'} feeds, start = Feed.index(user1, options) feeds.length.should == 1 - feeds[0].music_session_history.should == claimed_recording1.recording.music_session.music_session_history + feeds[0].music_session.should == claimed_recording1.recording.music_session.music_session options[:start] = start feeds, start = Feed.index(user1, options) @@ -235,12 +235,12 @@ describe Feed do it "supports plays pagination" do claimed_recording1 = FactoryGirl.create(:claimed_recording) - FactoryGirl.create(:playable_play, playable: claimed_recording1.recording.music_session.music_session_history, user: user1) + FactoryGirl.create(:playable_play, playable: claimed_recording1.recording.music_session.music_session, user: user1) options = {limit: 1, sort: 'plays'} feeds, start = Feed.index(user1, options) feeds.length.should == 1 - feeds[0].music_session_history.should == claimed_recording1.recording.music_session.music_session_history + feeds[0].music_session.should == claimed_recording1.recording.music_session.music_session options[:start] = start feeds, start = Feed.index(user1, options) @@ -263,8 +263,8 @@ describe Feed do feeds, start = Feed.index(claimed_recording1.user) feeds.length.should == 1 - claimed_recording1.recording.music_session.fan_access = false - claimed_recording1.recording.music_session.save! + claimed_recording1.recording.music_session.music_session.fan_access = false + claimed_recording1.recording.music_session.music_session.save! feeds, start = Feed.index(claimed_recording1.user) feeds.length.should == 0 @@ -274,8 +274,8 @@ describe Feed do describe "band feeds" do it "does show other band's stuff in this feed" do other_band = FactoryGirl.create(:band) - music_session = FactoryGirl.create(:music_session, band: other_band) - FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => user1) + music_session = FactoryGirl.create(:active_music_session, band: other_band) + FactoryGirl.create(:music_session_user_history, :history => music_session.music_session, :user => user1) claimed_recording1 = FactoryGirl.create(:claimed_recording) claimed_recording1.is_public = true @@ -290,28 +290,28 @@ describe Feed do it "shows public recordings to you and to others" do user1.bands << band user1.save! - music_session = FactoryGirl.create(:music_session, band: band) - music_session.music_session_history.fan_access.should be_true + music_session = FactoryGirl.create(:active_music_session, band: band) + music_session.music_session.fan_access.should be_true feeds, start = Feed.index(user1, band: band.id) feeds.length.should == 1 - feeds[0].music_session_history.should == music_session.music_session_history + feeds[0].music_session.should == music_session.music_session feeds, start = Feed.index(user2, band: band.id) feeds.length.should == 1 - feeds[0].music_session_history.should == music_session.music_session_history + feeds[0].music_session.should == music_session.music_session end it "shows private sessions to you, not to others" do user1.bands << band user1.save! - music_session = FactoryGirl.create(:music_session, band: band, fan_access: false) - music_session.music_session_history.fan_access.should be_false + music_session = FactoryGirl.create(:active_music_session, band: band, fan_access: false) + music_session.music_session.fan_access.should be_false feeds, start = Feed.index(user1, band: band.id) feeds.length.should == 1 - feeds[0].music_session_history.should == music_session.music_session_history - feeds[0].music_session_history.fan_access.should be_false + feeds[0].music_session.should == music_session.music_session + feeds[0].music_session.fan_access.should be_false feeds, start = Feed.index(user2, band: band.id) @@ -355,8 +355,8 @@ describe Feed do describe "user feeds" do it "does not show stuff from other people" do - music_session = FactoryGirl.create(:music_session) - FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => user2) + music_session = FactoryGirl.create(:active_music_session) + FactoryGirl.create(:music_session_user_history, :history => music_session.music_session, :user => user2) claimed_recording1 = FactoryGirl.create(:claimed_recording) claimed_recording1.is_public = true @@ -367,28 +367,28 @@ describe Feed do end it "shows public sessions to you and to others" do - music_session = FactoryGirl.create(:music_session) - FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => user1) + music_session = FactoryGirl.create(:active_music_session) + FactoryGirl.create(:music_session_user_history, :history => music_session.music_session, :user => user1) feeds, start = Feed.index(user1, user: user1.id) feeds.length.should == 1 - feeds[0].music_session_history.should == music_session.music_session_history + feeds[0].music_session.should == music_session.music_session feeds, start = Feed.index(user2, user: user1.id) feeds.length.should == 1 - feeds[0].music_session_history.should == music_session.music_session_history + feeds[0].music_session.should == music_session.music_session end it "shows private sessions to you, not to others" do - music_session = FactoryGirl.create(:music_session, fan_access: false) - music_session.music_session_history.fan_access.should be_false - FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => user1) + music_session = FactoryGirl.create(:active_music_session, fan_access: false) + music_session.music_session.fan_access.should be_false + FactoryGirl.create(:music_session_user_history, :history => music_session.music_session, :user => user1) feeds, start = Feed.index(user1, user: user1.id) feeds.length.should == 1 - feeds[0].music_session_history.should == music_session.music_session_history - feeds[0].music_session_history.fan_access.should be_false + feeds[0].music_session.should == music_session.music_session + feeds[0].music_session.fan_access.should be_false feeds, start = Feed.index(user2, user: user1.id) diff --git a/ruby/spec/jam_ruby/models/icecast_mount_spec.rb b/ruby/spec/jam_ruby/models/icecast_mount_spec.rb index 990db1902..69028ad03 100644 --- a/ruby/spec/jam_ruby/models/icecast_mount_spec.rb +++ b/ruby/spec/jam_ruby/models/icecast_mount_spec.rb @@ -166,39 +166,39 @@ describe IcecastMount do let(:server1) {FactoryGirl.create(:icecast_server_minimal)} let(:server2) {FactoryGirl.create(:icecast_server_with_overrides)} let(:server3) {FactoryGirl.create(:icecast_server_with_overrides)} - let(:hidden_music_session) { FactoryGirl.create(:music_session, :fan_access => false)} - let(:public_music_session) { FactoryGirl.create(:music_session, :fan_access => true)} - let(:public_music_session2) { FactoryGirl.create(:music_session, :fan_access => true)} - let(:public_music_session3) { FactoryGirl.create(:music_session, :fan_access => true)} + let(:hidden_music_session) { FactoryGirl.create(:active_music_session, :fan_access => false)} + let(:public_music_session) { FactoryGirl.create(:active_music_session, :fan_access => true)} + let(:public_music_session2) { FactoryGirl.create(:active_music_session, :fan_access => true)} + let(:public_music_session3) { FactoryGirl.create(:active_music_session, :fan_access => true)} before(:each) do end it "no fan access means no mount" do - mount = IcecastMount.build_session_mount(hidden_music_session, IcecastServer.find_best_server_for_user(hidden_music_session.creator)) + mount = IcecastMount.build_session_mount(hidden_music_session.music_session, hidden_music_session, IcecastServer.find_best_server_for_user(hidden_music_session.creator)) mount.should be_nil end it "with no servers" do IcecastServer.count.should == 0 - mount = IcecastMount.build_session_mount(public_music_session, IcecastServer.find_best_server_for_user(public_music_session.creator)) + mount = IcecastMount.build_session_mount(public_music_session.music_session, public_music_session, IcecastServer.find_best_server_for_user(public_music_session.creator)) mount.should be_nil end it "with a server that has a mount template" do server1.mount_template.should_not be_nil - mount = IcecastMount.build_session_mount(public_music_session, IcecastServer.find_best_server_for_user(public_music_session.creator)) + mount = IcecastMount.build_session_mount(public_music_session.music_session, public_music_session, IcecastServer.find_best_server_for_user(public_music_session.creator)) mount.should_not be_nil mount.save! end it "with a server that already has an associated mount" do server1.mount_template.should_not be_nil - mount = IcecastMount.build_session_mount(public_music_session, IcecastServer.find_best_server_for_user(public_music_session.creator)) + mount = IcecastMount.build_session_mount(public_music_session.music_session, public_music_session, IcecastServer.find_best_server_for_user(public_music_session.creator)) mount.save! - mount = IcecastMount.build_session_mount(public_music_session2, IcecastServer.find_best_server_for_user(public_music_session2.creator)) + mount = IcecastMount.build_session_mount(public_music_session2.music_session, public_music_session2, IcecastServer.find_best_server_for_user(public_music_session2.creator)) mount.save! server1.reload server1.mounts.length.should == 2 @@ -207,13 +207,13 @@ describe IcecastMount do it "picks a second server once the 1st has been chosen" do server1.touch - mount = IcecastMount.build_session_mount(public_music_session, IcecastServer.find_best_server_for_user(public_music_session.creator)) + mount = IcecastMount.build_session_mount(public_music_session.music_session, public_music_session, IcecastServer.find_best_server_for_user(public_music_session.creator)) mount.listeners = 1 # affect the weight mount.save! server2.touch - mount = IcecastMount.build_session_mount(public_music_session2, IcecastServer.find_best_server_for_user(public_music_session2.creator)) + mount = IcecastMount.build_session_mount(public_music_session2.music_session, public_music_session2, IcecastServer.find_best_server_for_user(public_music_session2.creator)) mount.save! server1.reload server1.mounts.length.should == 1 @@ -224,17 +224,17 @@ describe IcecastMount do it "picks the 1st server again once the 2nd has higher weight" do server1.touch - mount = IcecastMount.build_session_mount(public_music_session, IcecastServer.find_best_server_for_user(public_music_session.creator)) + mount = IcecastMount.build_session_mount(public_music_session.music_session, public_music_session, IcecastServer.find_best_server_for_user(public_music_session.creator)) mount.listeners = 1 # affect the weight mount.save! server2.touch - mount = IcecastMount.build_session_mount(public_music_session2, IcecastServer.find_best_server_for_user(public_music_session2.creator)) + mount = IcecastMount.build_session_mount(public_music_session2.music_session, public_music_session2, IcecastServer.find_best_server_for_user(public_music_session2.creator)) mount.sourced = 1 mount.save! - mount = IcecastMount.build_session_mount(public_music_session3, IcecastServer.find_best_server_for_user(public_music_session3.creator)) + mount = IcecastMount.build_session_mount(public_music_session3.music_session, public_music_session3, IcecastServer.find_best_server_for_user(public_music_session3.creator)) mount.listeners = 1 mount.save! diff --git a/ruby/spec/jam_ruby/models/icecast_mount_template_spec.rb b/ruby/spec/jam_ruby/models/icecast_mount_template_spec.rb index 7470219d7..c094b14dc 100644 --- a/ruby/spec/jam_ruby/models/icecast_mount_template_spec.rb +++ b/ruby/spec/jam_ruby/models/icecast_mount_template_spec.rb @@ -10,11 +10,11 @@ describe IcecastMountTemplate do describe "poke configs" do let(:server) { a = FactoryGirl.create(:icecast_server_with_overrides); a.config_updated; IcecastServer.find(a.id) } - let(:music_session) { FactoryGirl.create(:music_session, :fan_access => true)} + let(:music_session) { FactoryGirl.create(:active_music_session, :fan_access => true)} before(:each) do server.touch - mount = IcecastMount.build_session_mount(music_session, IcecastServer.find_best_server_for_user(music_session.creator)) + mount = IcecastMount.build_session_mount(music_session.music_session, music_session, IcecastServer.find_best_server_for_user(music_session.creator)) mount.save! server.save! server.config_updated diff --git a/ruby/spec/jam_ruby/models/invitation_spec.rb b/ruby/spec/jam_ruby/models/invitation_spec.rb index b5e1cb07f..baf02eca7 100644 --- a/ruby/spec/jam_ruby/models/invitation_spec.rb +++ b/ruby/spec/jam_ruby/models/invitation_spec.rb @@ -1,18 +1,18 @@ require 'spec_helper' -describe MusicSession do +describe ActiveMusicSession do it 'cant create invitation to non-friend' do user1 = FactoryGirl.create(:user) # in the jam session user2 = FactoryGirl.create(:user) # in the jam session - music_session = FactoryGirl.create(:music_session, :creator => user1) + music_session = FactoryGirl.create(:active_music_session, :creator => user1) music_session_member1 = FactoryGirl.create(:connection, :user => user1, :music_session => music_session, :ip_address => "1.1.1.1", :client_id => "1") music_session_member2 = FactoryGirl.create(:connection, :user => user2, :music_session => music_session, :ip_address => "2.2.2.2", :client_id => "2") - invitation = Invitation.new(:sender => user1, :receiver => user2, :music_session => music_session) + invitation = Invitation.new(:sender => user1, :receiver => user2, :music_session => music_session.music_session) invitation.save.should be_false invitation.errors.size.should == 1 @@ -24,7 +24,7 @@ describe MusicSession do user1 = FactoryGirl.create(:user) # in the jam session user2 = FactoryGirl.create(:user) # in the jam session - music_session = FactoryGirl.create(:music_session, :creator => user1) + music_session = FactoryGirl.create(:active_music_session, :creator => user1) music_session_member1 = FactoryGirl.create(:connection, :user => user1, :music_session => music_session, :ip_address => "1.1.1.1", :client_id => "1") music_session_member2 = FactoryGirl.create(:connection, :user => user2, :music_session => music_session, :ip_address => "2.2.2.2", :client_id => "2") @@ -32,7 +32,7 @@ describe MusicSession do FactoryGirl.create(:friendship, :user => user1, :friend => user2) FactoryGirl.create(:friendship, :user => user2, :friend => user1) - invitation = Invitation.new(:sender => user1, :receiver => user2, :music_session => music_session) + invitation = Invitation.new(:sender => user1, :receiver => user2, :music_session => music_session.music_session) invitation.save.should be_true end @@ -41,14 +41,14 @@ describe MusicSession do user1 = FactoryGirl.create(:user) # in the jam session user2 = FactoryGirl.create(:user) # in the jam session - music_session = FactoryGirl.create(:music_session, :creator => user1) + music_session = FactoryGirl.create(:active_music_session, :creator => user1) music_session_member1 = FactoryGirl.create(:connection, :user => user1, :music_session => music_session, :ip_address => "1.1.1.1", :client_id => "1") connection2 = FactoryGirl.create(:connection, :user => user2, :ip_address => "2.2.2.2", :client_id => "2") - join_request = FactoryGirl.create(:join_request, :user => user2, :music_session => music_session) + join_request = FactoryGirl.create(:join_request, :user => user2, :music_session => music_session.music_session) - invitation = Invitation.new(:sender => user1, :receiver => user2, :music_session => music_session, :join_request => join_request) + invitation = Invitation.new(:sender => user1, :receiver => user2, :music_session => music_session.music_session, :join_request => join_request) invitation.save.should be_true end @@ -57,15 +57,15 @@ describe MusicSession do user1 = FactoryGirl.create(:user) # in the jam session user2 = FactoryGirl.create(:user) # in the jam session - music_session = FactoryGirl.create(:music_session, :creator => user1) - music_session2 = FactoryGirl.create(:music_session, :creator => user1) + music_session = FactoryGirl.create(:active_music_session, :creator => user1) + music_session2 = FactoryGirl.create(:active_music_session, :creator => user1) music_session_member1 = FactoryGirl.create(:connection, :user => user1, :music_session => music_session, :ip_address => "1.1.1.1", :client_id => "1") connection2 = FactoryGirl.create(:connection, :user => user2, :ip_address => "2.2.2.2", :client_id => "2") - join_request = FactoryGirl.create(:join_request, :user => user2, :music_session => music_session2) + join_request = FactoryGirl.create(:join_request, :user => user2, :music_session => music_session2.music_session) - invitation = Invitation.new(:sender => user1, :receiver => user2, :music_session => music_session, :join_request => join_request) + invitation = Invitation.new(:sender => user1, :receiver => user2, :music_session => music_session.music_session, :join_request => join_request) invitation.save.should be_false invitation.errors.get(:join_request).should == [Invitation::JOIN_REQUEST_IS_NOT_FOR_RECEIVER_AND_MUSIC_SESSION ] diff --git a/ruby/spec/jam_ruby/models/join_request_spec.rb b/ruby/spec/jam_ruby/models/join_request_spec.rb index 14412556f..bfdd1a92b 100644 --- a/ruby/spec/jam_ruby/models/join_request_spec.rb +++ b/ruby/spec/jam_ruby/models/join_request_spec.rb @@ -4,9 +4,9 @@ describe JoinRequest do it 'can create a join request' do user1 = FactoryGirl.create(:user) - music_session = FactoryGirl.create(:music_session, :creator => user1) + music_session = FactoryGirl.create(:active_music_session, :creator => user1) music_session_member1 = FactoryGirl.create(:connection, :user => user1, :music_session => music_session) - join_request = JoinRequest.new(:user => user1, :music_session => music_session, :text => "Let me join yo") + join_request = JoinRequest.new(:user => user1, :music_session => music_session.music_session, :text => "Let me join yo") join_request.save.should be_true @@ -18,9 +18,9 @@ describe JoinRequest do it 'fans cant create a join request' do user1 = FactoryGirl.create(:user, :musician => true) user2 = FactoryGirl.create(:user, :musician => false) - music_session = FactoryGirl.create(:music_session, :creator => user1) + music_session = FactoryGirl.create(:active_music_session, :creator => user1) music_session_member1 = FactoryGirl.create(:connection, :user => user1, :music_session => music_session) - join_request = JoinRequest.new(:user => user2, :music_session => music_session, :text => "Let me join yo") + join_request = JoinRequest.new(:user => user2, :music_session => music_session.music_session, :text => "Let me join yo") join_request.save.should be_false join_request.errors.size.should == 1 @@ -29,12 +29,12 @@ describe JoinRequest do it 'cant create a dup join_request' do user1 = FactoryGirl.create(:user) - music_session = FactoryGirl.create(:music_session, :creator => user1) + music_session = FactoryGirl.create(:active_music_session, :creator => user1) music_session_member1 = FactoryGirl.create(:connection, :user => user1, :music_session => music_session) - join_request = JoinRequest.new(:user => user1, :music_session => music_session, :text => "Let me join yo") + join_request = JoinRequest.new(:user => user1, :music_session => music_session.music_session, :text => "Let me join yo") join_request.save.should be_true - join_request2 = JoinRequest.new(:user => user1, :music_session => music_session, :text => "Let me join yo") + join_request2 = JoinRequest.new(:user => user1, :music_session => music_session.music_session, :text => "Let me join yo") join_request2.save.should be_false join_request2.errors.get(:user_id) == ["has already been taken"] @@ -42,9 +42,9 @@ describe JoinRequest do it "cant contain profanity in the text" do user1 = FactoryGirl.create(:user) - music_session = FactoryGirl.create(:music_session, :creator => user1) + music_session = FactoryGirl.create(:active_music_session, :creator => user1) music_session_member1 = FactoryGirl.create(:connection, :user => user1, :music_session => music_session) - join_request = JoinRequest.new(:user => user1, :music_session => music_session, :text => "fuck you") + join_request = JoinRequest.new(:user => user1, :music_session => music_session.music_session, :text => "fuck you") join_request.save join_request.valid?.should be_false end diff --git a/ruby/spec/jam_ruby/models/mix_spec.rb b/ruby/spec/jam_ruby/models/mix_spec.rb index 7acb05f81..eaa65bd63 100755 --- a/ruby/spec/jam_ruby/models/mix_spec.rb +++ b/ruby/spec/jam_ruby/models/mix_spec.rb @@ -7,7 +7,7 @@ describe Mix do @connection = FactoryGirl.create(:connection, :user => @user) @instrument = FactoryGirl.create(:instrument, :description => 'a great instrument') @track = FactoryGirl.create(:track, :connection => @connection, :instrument => @instrument) - @music_session = FactoryGirl.create(:music_session, :creator => @user, :musician_access => true) + @music_session = FactoryGirl.create(:active_music_session, :creator => @user, :musician_access => true) # @music_session.connections << @connection @music_session.save @connection.join_the_session(@music_session, true, nil) diff --git a/ruby/spec/jam_ruby/models/music_session_history_spec.rb b/ruby/spec/jam_ruby/models/music_session_history_spec.rb index 11fe4a0a5..ba36c9a30 100644 --- a/ruby/spec/jam_ruby/models/music_session_history_spec.rb +++ b/ruby/spec/jam_ruby/models/music_session_history_spec.rb @@ -1,23 +1,57 @@ require 'spec_helper' -describe MusicSessionHistory do +describe MusicSession do + let(:creator) {FactoryGirl.create(:user)} let(:some_user) { FactoryGirl.create(:user) } - let(:music_session) { FactoryGirl.create(:music_session_no_history) } - let(:user_history1) { FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => music_session.creator, :created_at => 2.days.ago, :session_removed_at => 1.days.ago) } - let(:user_history2) { FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => some_user, :created_at => 2.days.ago, :session_removed_at => 1.days.ago) } - let(:user_history3) { FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => music_session.creator, :created_at => 3.days.ago, :session_removed_at => 2.days.ago) } - let(:user_history4) { FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => some_user, :created_at => 3.days.ago, :session_removed_at => 2.days.ago) } + let(:music_session) { FactoryGirl.create(:active_music_session_no_user_history) } + let(:user_history1) { FactoryGirl.create(:music_session_user_history, :history => music_session.music_session, :user => music_session.creator, :created_at => 2.days.ago, :session_removed_at => 1.days.ago) } + let(:user_history2) { FactoryGirl.create(:music_session_user_history, :history => music_session.music_session, :user => some_user, :created_at => 2.days.ago, :session_removed_at => 1.days.ago) } + let(:user_history3) { FactoryGirl.create(:music_session_user_history, :history => music_session.music_session, :user => music_session.creator, :created_at => 3.days.ago, :session_removed_at => 2.days.ago) } + let(:user_history4) { FactoryGirl.create(:music_session_user_history, :history => music_session.music_session, :user => some_user, :created_at => 3.days.ago, :session_removed_at => 2.days.ago) } - it "create" do - music_session.music_session_history.description.should eql(music_session.description) + describe "validations" do + it "genre must be set" do + music_session = FactoryGirl.build(:music_session) + music_session.genre = nil + music_session.save.should be_false + music_session.errors[:genre].should == ["can't be blank"] + end + + it "updates the fields of a music session properly" do + genre1 = FactoryGirl.create(:genre) + genre2 = FactoryGirl.create(:genre) + genre3 = FactoryGirl.create(:genre) + genre4 = FactoryGirl.create(:genre) + creator = FactoryGirl.create(:user) + session = FactoryGirl.create(:music_session, :creator => creator, :description => "Session", :genre => genre3) + session.update_attributes({:description => "Session2", :genre => genre1}) + session.reload + session.description.should == "Session2" + session.genre.should == genre1 + end + + it "must have legal_terms accepted" do + user1 = FactoryGirl.create(:user) + music_session = FactoryGirl.build(:music_session, :creator => user1, legal_terms: false) + music_session.save + music_session.valid?.should be_false + music_session.errors["legal_terms"].should == ["is not included in the list"] + end + + it "cannot have profanity in the description" do + user1 = FactoryGirl.create(:user) + music_session = FactoryGirl.build(:music_session, :creator => user1, legal_terms: false, :description => "fuck you") + music_session.save + music_session.valid?.should be_false + end end it "unique users" do user_history1.should_not be_nil user_history2.should_not be_nil - users = music_session.music_session_history.unique_users + users = music_session.music_session.unique_users users.length.should eql(2) @@ -26,7 +60,7 @@ describe MusicSessionHistory do user_history3.should_not be_nil user_history4.should_not be_nil - users = music_session.music_session_history.unique_users + users = music_session.music_session.unique_users users.length.should eql(2) users.include?(some_user).should be_true @@ -44,7 +78,7 @@ describe MusicSessionHistory do user_history2.session_removed_at = session_removed_at user_history2.save! - histories = music_session.music_session_history.unique_user_histories + histories = music_session.music_session.unique_user_histories histories.length.should eql(2) histories[0].first_name.should_not be_nil histories[0].last_name.should_not be_nil @@ -62,7 +96,7 @@ describe MusicSessionHistory do user_history4.session_removed_at = session_removed_at user_history4.save! - histories = music_session.music_session_history.unique_user_histories + histories = music_session.music_session.unique_user_histories histories.length.should eql(2) histories[0].total_duration.to_i.should == 2.day.to_i histories[0].total_instruments.should == 'guitar|guitar' diff --git a/ruby/spec/jam_ruby/models/music_session_perf_data_spec.rb b/ruby/spec/jam_ruby/models/music_session_perf_data_spec.rb index 2ec72ba71..b39bf07a1 100644 --- a/ruby/spec/jam_ruby/models/music_session_perf_data_spec.rb +++ b/ruby/spec/jam_ruby/models/music_session_perf_data_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe MusicSessionPerfData do before do - #music_session = FactoryGirl.create(:music_session) + #music_session = FactoryGirl.create(:active_music_session) #connection = FactoryGirl.create(:connection, :music_session => music_session) end diff --git a/ruby/spec/jam_ruby/models/music_session_spec.rb b/ruby/spec/jam_ruby/models/music_session_spec.rb index f76e264a8..74be88bf1 100644 --- a/ruby/spec/jam_ruby/models/music_session_spec.rb +++ b/ruby/spec/jam_ruby/models/music_session_spec.rb @@ -1,28 +1,20 @@ require 'spec_helper' -describe MusicSession do +describe ActiveMusicSession do before(:each) do - MusicSession.delete_all + ActiveMusicSession.delete_all IcecastServer.delete_all IcecastMount.delete_all end - describe "validations" do - it "genre must be set" do - music_session = FactoryGirl.build(:music_session) - music_session.genres = [] - music_session.save.should be_false - music_session.errors[:genres].should == [ValidationMessages::SESSION_GENRE_MINIMUM_NOT_MET] - end - end it 'can grant access to valid user' do user1 = FactoryGirl.create(:user) # in the jam session user2 = FactoryGirl.create(:user) # in the jam session user3 = FactoryGirl.create(:user) # not in the jam session - music_session = FactoryGirl.create(:music_session, :creator => user1, :musician_access => false) + music_session = FactoryGirl.create(:active_music_session, :creator => user1, :musician_access => false) FactoryGirl.create(:connection, :user => user1, :music_session => music_session) FactoryGirl.create(:connection, :user => user2, :music_session => music_session) @@ -38,7 +30,7 @@ describe MusicSession do user2 = FactoryGirl.create(:user) # in the jam session user3 = FactoryGirl.create(:user) # not in the jam session - music_session = FactoryGirl.create(:music_session, :creator => user1, :musician_access => true) + music_session = FactoryGirl.create(:active_music_session, :creator => user1, :musician_access => true) music_session.can_join?(user1, true).should == true music_session.can_join?(user2, true).should == true @@ -50,7 +42,7 @@ describe MusicSession do user2 = FactoryGirl.create(:user) # in the jam session user3 = FactoryGirl.create(:user) # not in the jam session - music_session = FactoryGirl.create(:music_session, :creator => user1, :musician_access => false) + music_session = FactoryGirl.create(:active_music_session, :creator => user1, :musician_access => false) FactoryGirl.create(:connection, :user => user1, :music_session => music_session) music_session.can_join?(user1, true).should == true @@ -60,7 +52,7 @@ describe MusicSession do # invite user 2 FactoryGirl.create(:friendship, :user => user1, :friend => user2) FactoryGirl.create(:friendship, :user => user2, :friend => user1) - FactoryGirl.create(:invitation, :sender => user1, :receiver => user2, :music_session => music_session) + FactoryGirl.create(:invitation, :sender => user1, :receiver => user2, :music_session => music_session.music_session) music_session.can_join?(user1, true).should == true music_session.can_join?(user2, true).should == true @@ -72,7 +64,7 @@ describe MusicSession do user2 = FactoryGirl.create(:user) # in the jam session user3 = FactoryGirl.create(:user) # not in the jam session - music_session = FactoryGirl.create(:music_session, :creator => user1, :musician_access => false, :fan_access => false) + music_session = FactoryGirl.create(:active_music_session, :creator => user1, :musician_access => false, :fan_access => false) FactoryGirl.create(:connection, :user => user1, :music_session => music_session) music_session.can_see?(user1).should == true @@ -82,29 +74,28 @@ describe MusicSession do # invite user 2 FactoryGirl.create(:friendship, :user => user1, :friend => user2) FactoryGirl.create(:friendship, :user => user2, :friend => user1) - FactoryGirl.create(:invitation, :sender => user1, :receiver => user2, :music_session => music_session) + FactoryGirl.create(:invitation, :sender => user1, :receiver => user2, :music_session => music_session.music_session) music_session.can_see?(user1).should == true music_session.can_see?(user2).should == true music_session.can_see?(user3).should == false end - describe "index" do it "orders two sessions by created_at starting with most recent" do creator = FactoryGirl.create(:user) creator2 = FactoryGirl.create(:user) - earlier_session = FactoryGirl.create(:music_session, :creator => creator, :description => "Earlier Session") + earlier_session = FactoryGirl.create(:active_music_session, :creator => creator, :description => "Earlier Session") FactoryGirl.create(:connection, :user => creator, :music_session => earlier_session) - later_session = FactoryGirl.create(:music_session, :creator => creator2, :description => "Later Session") + later_session = FactoryGirl.create(:active_music_session, :creator => creator2, :description => "Later Session") FactoryGirl.create(:connection, :user => creator2, :music_session => later_session) user = FactoryGirl.create(:user) #ActiveRecord::Base.logger = Logger.new(STDOUT) - music_sessions = MusicSession.index(user) + music_sessions = ActiveMusicSession.index(user) music_sessions.length.should == 2 music_sessions.first.id.should == later_session.id end @@ -113,17 +104,17 @@ describe MusicSession do creator1 = FactoryGirl.create(:user) creator2 = FactoryGirl.create(:user) - earlier_session = FactoryGirl.create(:music_session, :creator => creator1, :description => "Earlier Session") + earlier_session = FactoryGirl.create(:active_music_session, :creator => creator1, :description => "Earlier Session") FactoryGirl.create(:connection, :user => creator1, :music_session => earlier_session) - later_session = FactoryGirl.create(:music_session, :creator => creator2, :description => "Later Session") + later_session = FactoryGirl.create(:active_music_session, :creator => creator2, :description => "Later Session") FactoryGirl.create(:connection, :user => creator2, :music_session => later_session) user = FactoryGirl.create(:user) FactoryGirl.create(:connection, :user => creator1, :music_session => earlier_session) FactoryGirl.create(:friendship, :user => creator1, :friend => user) FactoryGirl.create(:friendship, :user => user, :friend => creator1) - FactoryGirl.create(:invitation, :sender => creator1, :receiver => user, :music_session => earlier_session) + FactoryGirl.create(:invitation, :sender => creator1, :receiver => user, :music_session => earlier_session.music_session) - music_sessions = MusicSession.index(user) + music_sessions = ActiveMusicSession.index(user) music_sessions.length.should == 2 music_sessions.first.id.should == earlier_session.id end @@ -133,9 +124,9 @@ describe MusicSession do creator1 = FactoryGirl.create(:user) creator2 = FactoryGirl.create(:user) - earlier_session = FactoryGirl.create(:music_session, :creator => creator1, :description => "Earlier Session") + earlier_session = FactoryGirl.create(:active_music_session, :creator => creator1, :description => "Earlier Session") FactoryGirl.create(:connection, :user => creator1, :music_session => earlier_session) - later_session = FactoryGirl.create(:music_session, :creator => creator2, :description => "Later Session") + later_session = FactoryGirl.create(:active_music_session, :creator => creator2, :description => "Later Session") FactoryGirl.create(:connection, :user => creator2, :music_session => later_session) user = FactoryGirl.create(:user) @@ -144,41 +135,41 @@ describe MusicSession do FactoryGirl.create(:connection, :user => creator1, :music_session => earlier_session) FactoryGirl.create(:connection, :user => creator2, :music_session => earlier_session) - music_sessions = MusicSession.index(user) + music_sessions = ActiveMusicSession.index(user) music_sessions.length.should == 2 music_sessions.first.id.should == earlier_session.id end it "doesn't list a session if musician_access is set to false" do creator = FactoryGirl.create(:user) - session = FactoryGirl.create(:music_session, :creator => creator, :description => "Session", :musician_access => false) + session = FactoryGirl.create(:active_music_session, :creator => creator, :description => "Session", :musician_access => false) user = FactoryGirl.create(:user) - music_sessions = MusicSession.index(user) + music_sessions = ActiveMusicSession.index(user) music_sessions.length.should == 0 end it "does list a session if musician_access is set to false but user was invited" do creator = FactoryGirl.create(:user) - session = FactoryGirl.create(:music_session, :creator => creator, :description => "Session", :musician_access => false) + session = FactoryGirl.create(:active_music_session, :creator => creator, :description => "Session", :musician_access => false) user = FactoryGirl.create(:user) FactoryGirl.create(:connection, :user => creator, :music_session => session) FactoryGirl.create(:friendship, :user => creator, :friend => user) FactoryGirl.create(:friendship, :user => user, :friend => creator) - FactoryGirl.create(:invitation, :sender => creator, :receiver => user, :music_session => session) + FactoryGirl.create(:invitation, :sender => creator, :receiver => user, :music_session => session.music_session) - music_sessions = MusicSession.index(user) + music_sessions = ActiveMusicSession.index(user) music_sessions.length.should == 1 end it "lists a session if the genre matches" do creator = FactoryGirl.create(:user) genre = FactoryGirl.create(:genre) - session = FactoryGirl.create(:music_session, :creator => creator, :description => "Session", :genres => [genre]) + session = FactoryGirl.create(:active_music_session, :creator => creator, :description => "Session", :genre => genre) FactoryGirl.create(:connection, :user => creator, :music_session => session) user = FactoryGirl.create(:user) - music_sessions = MusicSession.index(user, genres: [genre.id]) + music_sessions = ActiveMusicSession.index(user, genres: [genre.id]) music_sessions.length.should == 1 end @@ -186,37 +177,37 @@ describe MusicSession do creator = FactoryGirl.create(:user) genre1 = FactoryGirl.create(:genre) genre2 = FactoryGirl.create(:genre) - session = FactoryGirl.create(:music_session, :creator => creator, :description => "Session", :genres => [genre1]) + session = FactoryGirl.create(:active_music_session, :creator => creator, :description => "Session", :genre => genre1) user = FactoryGirl.create(:user) - music_sessions = MusicSession.index(user, genres: [genre2.id]) + music_sessions = ActiveMusicSession.index(user, genres: [genre2.id]) music_sessions.length.should == 0 end it "does not list a session if friends_only is set and no friends are in it" do creator = FactoryGirl.create(:user) - session = FactoryGirl.create(:music_session, :creator => creator, :description => "Session") + session = FactoryGirl.create(:active_music_session, :creator => creator, :description => "Session") user = FactoryGirl.create(:user) - music_sessions = MusicSession.index(user, friends_only: true) + music_sessions = ActiveMusicSession.index(user, friends_only: true) music_sessions.length.should == 0 end it "lists a session properly if a friend is in it" do creator = FactoryGirl.create(:user) - session = FactoryGirl.create(:music_session, :creator => creator, :description => "Session") + session = FactoryGirl.create(:active_music_session, :creator => creator, :description => "Session") user = FactoryGirl.create(:user) FactoryGirl.create(:friendship, :user => creator, :friend => user) FactoryGirl.create(:friendship, :user => user, :friend => creator) FactoryGirl.create(:connection, :user => creator, :music_session => session) - music_sessions = MusicSession.index(user) + music_sessions = ActiveMusicSession.index(user) music_sessions.length.should == 1 - music_sessions = MusicSession.index(user, friends_only: true) + music_sessions = ActiveMusicSession.index(user, friends_only: true) music_sessions.length.should == 1 - music_sessions = MusicSession.index(user, friends_only: false, my_bands_only: true) + music_sessions = ActiveMusicSession.index(user, friends_only: false, my_bands_only: true) music_sessions.length.should == 0 - music_sessions = MusicSession.index(user, friends_only: true, my_bands_only: true) + music_sessions = ActiveMusicSession.index(user, friends_only: true, my_bands_only: true) music_sessions.length.should == 1 end @@ -225,46 +216,46 @@ describe MusicSession do # however, this bug continually crops up so the .index method will protect against this common bug creator = FactoryGirl.create(:user) - session = FactoryGirl.create(:music_session, :creator => creator, :description => "Session") + session = FactoryGirl.create(:active_music_session, :creator => creator, :description => "Session") session.connections.delete_all # should leave a bogus, 0 participant session around - music_sessions = MusicSession.index(creator) + music_sessions = ActiveMusicSession.index(creator) music_sessions.length.should == 0 end it "does not list a session if my_bands_only is set and it's not my band" do creator = FactoryGirl.create(:user) - session = FactoryGirl.create(:music_session, :creator => creator, :description => "Session") + session = FactoryGirl.create(:active_music_session, :creator => creator, :description => "Session") user = FactoryGirl.create(:user) - music_sessions = MusicSession.index(user, friends_only: false, my_bands_only: true) + music_sessions = ActiveMusicSession.index(user, friends_only: false, my_bands_only: true) music_sessions.length.should == 0 end it "lists a session properly if it's my band's session" do band = FactoryGirl.create(:band) creator = FactoryGirl.create(:user) - session = FactoryGirl.create(:music_session, :creator => creator, :description => "Session", :band => band) + session = FactoryGirl.create(:active_music_session, :creator => creator, :description => "Session", :band => band) FactoryGirl.create(:connection, :user => creator, :music_session => session) user = FactoryGirl.create(:user) FactoryGirl.create(:band_musician, :band => band, :user => creator) FactoryGirl.create(:band_musician, :band => band, :user => user) - music_sessions = MusicSession.index(user) + music_sessions = ActiveMusicSession.index(user) music_sessions.length.should == 1 - music_sessions = MusicSession.index(user, friends_only: true) + music_sessions = ActiveMusicSession.index(user, friends_only: true) music_sessions.length.should == 0 - music_sessions = MusicSession.index(user, friends_only: false, my_bands_only: true) + music_sessions = ActiveMusicSession.index(user, friends_only: false, my_bands_only: true) music_sessions.length.should == 1 - music_sessions = MusicSession.index(user, friends_only: true, my_bands_only: true) + music_sessions = ActiveMusicSession.index(user, friends_only: true, my_bands_only: true) music_sessions.length.should == 1 end describe "index(as_musician: false)" do let(:fan_access) { true } let(:creator) { FactoryGirl.create(:user) } - let(:session) { FactoryGirl.create(:music_session, creator: creator, fan_access: fan_access ) } + let(:session) { FactoryGirl.create(:active_music_session, creator: creator, fan_access: fan_access ) } let(:connection) { FactoryGirl.create(:connection, user: creator, :music_session => session) } let(:user) {FactoryGirl.create(:user) } @@ -277,13 +268,13 @@ describe MusicSession do it "no session listed if mount is nil" do connection.touch - sessions = MusicSession.index(user, as_musician: false) + sessions = ActiveMusicSession.index(user, as_musician: false) sessions.length.should == 0 end end describe "with mount" do - let(:session_with_mount) { FactoryGirl.create(:music_session_with_mount) } + let(:session_with_mount) { FactoryGirl.create(:active_music_session_with_mount) } let(:connection_with_mount) { FactoryGirl.create(:connection, user: creator, :music_session => session_with_mount) } @@ -293,7 +284,7 @@ describe MusicSession do it "no session listed if icecast_server config hasn't been updated" do connection_with_mount.touch - sessions = MusicSession.index(user, as_musician: false) + sessions = ActiveMusicSession.index(user, as_musician: false) sessions.length.should == 0 end @@ -303,7 +294,7 @@ describe MusicSession do session_with_mount.save!(:validate => false) session_with_mount.mount.server.config_updated_at = 1.minute.ago session_with_mount.mount.server.save!(:validate => false) - sessions = MusicSession.index(user, as_musician: false) + sessions = ActiveMusicSession.index(user, as_musician: false) sessions.length.should == 1 end end @@ -316,10 +307,10 @@ describe MusicSession do creator = FactoryGirl.create(:user) creator2 = FactoryGirl.create(:user) - earlier_session = FactoryGirl.create(:music_session, :creator => creator, :description => "Earlier Session") + earlier_session = FactoryGirl.create(:active_music_session, :creator => creator, :description => "Earlier Session") c1 = FactoryGirl.create(:connection, user: creator, music_session: earlier_session, addr: 0x01020304, locidispid: 1) - later_session = FactoryGirl.create(:music_session, :creator => creator2, :description => "Later Session") + later_session = FactoryGirl.create(:active_music_session, :creator => creator2, :description => "Later Session") c2 = FactoryGirl.create(:connection, user: creator2, music_session: later_session, addr: 0x21020304, locidispid: 2) user = FactoryGirl.create(:user) @@ -331,7 +322,7 @@ describe MusicSession do # scores! #ActiveRecord::Base.logger = Logger.new(STDOUT) - music_sessions = MusicSession.nindex(user, client_id: c3.client_id).take(100) + music_sessions = ActiveMusicSession.nindex(user, client_id: c3.client_id).take(100) #music_sessions = MusicSession.index(user).take(100) #ActiveRecord::Base.logger = nil @@ -341,50 +332,22 @@ describe MusicSession do end end - it "updates the fields of a music session properly" do - genre1 = FactoryGirl.create(:genre) - genre2 = FactoryGirl.create(:genre) - genre3 = FactoryGirl.create(:genre) - genre4 = FactoryGirl.create(:genre) - creator = FactoryGirl.create(:user) - session = FactoryGirl.create(:music_session, :creator => creator, :description => "Session", :genres => [genre3,genre4]) - session.update_attributes({:description => "Session2", :genre => [genre1, genre2]}) - session.genres = [genre1, genre2] - session.reload - session.description.should == "Session2" - session.genres.length.should == 2 - session.genres[0].id.should == genre1.id - end it 'uninvited users cant join approval-required sessions without invitation' do user1 = FactoryGirl.create(:user) # in the jam session user2 = FactoryGirl.create(:user) # in the jam session - music_session = FactoryGirl.create(:music_session, :creator => user1, :musician_access => true, :approval_required => true) + music_session = FactoryGirl.create(:active_music_session, :creator => user1, :musician_access => true, :approval_required => true) connection1 = FactoryGirl.create(:connection, :user => user1, :music_session => music_session) expect { FactoryGirl.create(:connection, :user => user2, :music_session => music_session, :joining_session => true) }.to raise_error(ActiveRecord::RecordInvalid) end - it "must have legal_terms accepted" do - user1 = FactoryGirl.create(:user) - music_session = FactoryGirl.build(:music_session, :creator => user1, :legal_terms=> false) - music_session.save - music_session.valid?.should be_false - music_session.errors["legal_terms"].should == ["is not included in the list"] - end - - it "cannot have profanity in the description" do - user1 = FactoryGirl.create(:user) - music_session = FactoryGirl.build(:music_session, :creator => user1, :legal_terms=> false, :description => "fuck you") - music_session.save - music_session.valid?.should be_false - end it "is_recording? returns false if not recording" do user1 = FactoryGirl.create(:user) - music_session = FactoryGirl.build(:music_session, :creator => user1) + music_session = FactoryGirl.build(:active_music_session, :creator => user1) music_session.is_recording?.should be_false end @@ -395,7 +358,7 @@ describe MusicSession do @connection = FactoryGirl.create(:connection, :user => @user1) @instrument = FactoryGirl.create(:instrument, :description => 'a great instrument') @track = FactoryGirl.create(:track, :connection => @connection, :instrument => @instrument) - @music_session = FactoryGirl.create(:music_session, :creator => @user1, :musician_access => true) + @music_session = FactoryGirl.create(:active_music_session, :creator => @user1, :musician_access => true) # @music_session.connections << @connection @music_session.save! @connection.join_the_session(@music_session, true, nil) @@ -474,7 +437,7 @@ describe MusicSession do before(:each) do @user1 = FactoryGirl.create(:user) @user2 = FactoryGirl.create(:user) - @music_session = FactoryGirl.create(:music_session, :creator => @user1, :musician_access => true) + @music_session = FactoryGirl.create(:active_music_session, :creator => @user1, :musician_access => true) @connection1 = FactoryGirl.create(:connection, :user => @user1, :music_session => @music_session, :as_musician => true) @connection2 = FactoryGirl.create(:connection, :user => @user2, :music_session => @music_session, :as_musician => false) @@ -500,14 +463,5 @@ describe MusicSession do @music_session.get_connection_ids(exclude_client_id: @connection2.client_id, as_musician: true).should == [@connection1.client_id] end end - - - describe "autosave of music session history" do - it "is created on initial music session create" do - music_session = FactoryGirl.create(:music_session) - history = MusicSessionHistory.find(music_session.id) - history.genres.should == music_session.genres.first.id - end - end end diff --git a/ruby/spec/jam_ruby/models/music_sessions_user_history_spec.rb b/ruby/spec/jam_ruby/models/music_sessions_user_history_spec.rb index 3d7713b86..42415be4d 100644 --- a/ruby/spec/jam_ruby/models/music_sessions_user_history_spec.rb +++ b/ruby/spec/jam_ruby/models/music_sessions_user_history_spec.rb @@ -3,11 +3,12 @@ require 'spec_helper' describe MusicSessionUserHistory do let(:some_user) { FactoryGirl.create(:user) } - let(:music_session) { FactoryGirl.create(:music_session_no_history) } - let(:user_history1) { FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => music_session.creator) } - let(:user_history2) { FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => some_user) } + let(:music_session) { FactoryGirl.create(:active_music_session_no_user_history) } + let(:user_history1) { FactoryGirl.create(:music_session_user_history, :history => music_session.music_session, :user => music_session.creator) } + let(:user_history2) { FactoryGirl.create(:music_session_user_history, :history => music_session.music_session, :user => some_user) } describe "create" do + pending it {user_history1.music_session_id.should == music_session.id } it {user_history1.created_at.should_not be_nil } it {user_history1.session_removed_at.should be_nil } @@ -15,28 +16,35 @@ describe MusicSessionUserHistory do describe "rating" do - describe "success" do - - before(:each) do - user_history1.update_attribute(:rating ,0) - end - - it { user_history1.errors.any?.should be_false} + it "success" do + user_history1.update_attribute(:rating, 1) + expect( user_history1.errors.any? ).to eq(false) end - describe "out of range" do - before(:each) do - user_history1.update_attribute(:rating, 3) - user_history1.save - end + it "out of range" do + user_history1.rating = 2 + user_history1.save + expect( user_history1.errors.any? ).to eq(true) + end - it { user_history1.errors.any?.should be_true} + it 'should rate success' do + users = [user_history1, user_history2] + Timecop.travel(Time.now + (MusicSessionUserHistory::MIN_SESSION_DURATION_RATING * 1.5).seconds) + expect( user_history1.should_rate_session? ).to eq(true) + Timecop.return + end + + it 'should rate fails' do + users = [user_history1] + expect( user_history1.should_rate_session? ).to eq(false) + users = [user_history1, user_history2] + expect( user_history2.should_rate_session? ).to eq(false) end end describe "end_history" do - + pending it "histories created at the same time" do user_history1.reload user_history2.reload @@ -77,7 +85,7 @@ describe MusicSessionUserHistory do end it "two histories with same user within bounds of history1" do - user_history3 = FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => some_user) + user_history3 = FactoryGirl.create(:music_session_user_history, :history => music_session.music_session, :user => some_user) # if user2 comes and goes 2 times while user one is there, it shouldn't be a false 3 user_history1.session_removed_at = user_history1.created_at + 5 @@ -98,7 +106,7 @@ describe MusicSessionUserHistory do it "two histories with different user within bounds of history1" do third_user = FactoryGirl.create(:user); - user_history3 = FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => third_user) + user_history3 = FactoryGirl.create(:music_session_user_history, :history => music_session.music_session, :user => third_user) # if user2 comes and goes 2 times while user one is there, it shouldn't be a false 3 user_history1.session_removed_at = user_history1.created_at + 5 @@ -119,7 +127,7 @@ describe MusicSessionUserHistory do it "two overlapping histories with different user within bounds of history1" do third_user = FactoryGirl.create(:user); - user_history3 = FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => third_user) + user_history3 = FactoryGirl.create(:music_session_user_history, :history => music_session.music_session, :user => third_user) # if user2 comes and goes 2 times while user one is there, it shouldn't be a false 3 user_history1.session_removed_at = user_history1.created_at + 5 @@ -136,10 +144,7 @@ describe MusicSessionUserHistory do user_history1.end_history user_history1.max_concurrent_connections.should == 3 end - - end - end diff --git a/ruby/spec/jam_ruby/models/musician_search_spec.rb b/ruby/spec/jam_ruby/models/musician_search_spec.rb index a3c1bdf07..e957b9c46 100644 --- a/ruby/spec/jam_ruby/models/musician_search_spec.rb +++ b/ruby/spec/jam_ruby/models/musician_search_spec.rb @@ -112,7 +112,7 @@ describe 'Musician search' do connection = FactoryGirl.create(:connection, :user => usr) instrument = FactoryGirl.create(:instrument, :description => 'a great instrument') track = FactoryGirl.create(:track, :connection => connection, :instrument => instrument) - music_session = FactoryGirl.create(:music_session, :creator => usr, :musician_access => true) + music_session = FactoryGirl.create(:active_music_session, :creator => usr, :musician_access => true) # music_session.connections << connection music_session.save connection.join_the_session(music_session, true, nil) @@ -127,7 +127,7 @@ describe 'Musician search' do def make_session(usr) connection = FactoryGirl.create(:connection, :user => usr) - music_session = FactoryGirl.create(:music_session, :creator => usr, :musician_access => true) + music_session = FactoryGirl.create(:active_music_session, :creator => usr, :musician_access => true) # music_session.connections << connection music_session.save connection.join_the_session(music_session, true, nil) diff --git a/ruby/spec/jam_ruby/models/recorded_track_spec.rb b/ruby/spec/jam_ruby/models/recorded_track_spec.rb index 42c197bc9..002c14f29 100644 --- a/ruby/spec/jam_ruby/models/recorded_track_spec.rb +++ b/ruby/spec/jam_ruby/models/recorded_track_spec.rb @@ -9,7 +9,7 @@ describe RecordedTrack do @user = FactoryGirl.create(:user) @connection = FactoryGirl.create(:connection, :user => @user) @instrument = FactoryGirl.create(:instrument, :description => 'a great instrument') - @music_session = FactoryGirl.create(:music_session, :creator => @user, :musician_access => true) + @music_session = FactoryGirl.create(:active_music_session, :creator => @user, :musician_access => true) @track = FactoryGirl.create(:track, :connection => @connection, :instrument => @instrument) @recording = FactoryGirl.create(:recording, :music_session => @music_session, :owner => @user) end diff --git a/ruby/spec/jam_ruby/models/recording_spec.rb b/ruby/spec/jam_ruby/models/recording_spec.rb index 56ccf176a..f3966feb5 100644 --- a/ruby/spec/jam_ruby/models/recording_spec.rb +++ b/ruby/spec/jam_ruby/models/recording_spec.rb @@ -5,7 +5,7 @@ describe Recording do before do @user = FactoryGirl.create(:user) @instrument = FactoryGirl.create(:instrument, :description => 'a great instrument') - @music_session = FactoryGirl.create(:music_session, :creator => @user, :musician_access => true) + @music_session = FactoryGirl.create(:active_music_session, :creator => @user, :musician_access => true) @connection = FactoryGirl.create(:connection, :user => @user, :music_session => @music_session) @track = FactoryGirl.create(:track, :connection => @connection, :instrument => @instrument) end diff --git a/ruby/spec/jam_ruby/models/share_token_spec.rb b/ruby/spec/jam_ruby/models/share_token_spec.rb index 60abe3d08..4163e1bdf 100644 --- a/ruby/spec/jam_ruby/models/share_token_spec.rb +++ b/ruby/spec/jam_ruby/models/share_token_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe ShareToken do let(:user) { FactoryGirl.create(:user) } - let(:music_session) {FactoryGirl.create(:music_session) } + let(:music_session) {FactoryGirl.create(:active_music_session) } let(:claimed_recording) {FactoryGirl.create(:claimed_recording) } before(:each) do @@ -13,9 +13,9 @@ describe ShareToken do it "can reference a music session" do music_session.touch # should create a MSH, and a token, too ShareToken.count.should == 1 - music_session.music_session_history.share_token.should_not be_nil + music_session.music_session.share_token.should_not be_nil token = ShareToken.find_by_shareable_id!(music_session.id) - token.should == music_session.music_session_history.share_token + token.should == music_session.music_session.share_token token.shareable_id.should == music_session.id token.shareable_type.should == 'session' end diff --git a/ruby/spec/jam_ruby/models/track_spec.rb b/ruby/spec/jam_ruby/models/track_spec.rb index 8f59526d9..09f032f96 100644 --- a/ruby/spec/jam_ruby/models/track_spec.rb +++ b/ruby/spec/jam_ruby/models/track_spec.rb @@ -3,11 +3,11 @@ require 'spec_helper' describe Track do let (:user) {FactoryGirl.create(:user) } - let (:music_session) { FactoryGirl.create(:music_session, :creator => user)} + let (:music_session) { FactoryGirl.create(:active_music_session, :creator => user)} let (:connection) { FactoryGirl.create(:connection, :user => user, :music_session => music_session) } let (:track) { FactoryGirl.create(:track, :connection => connection)} let (:track2) { FactoryGirl.create(:track, :connection => connection)} - let (:msuh) {FactoryGirl.create(:music_session_user_history, :history => music_session.music_session_history, :user => user, :client_id => connection.client_id) } + let (:msuh) {FactoryGirl.create(:music_session_user_history, :history => music_session.music_session, :user => user, :client_id => connection.client_id) } let (:track_hash) { {:client_track_id => 'client_guid', :sound => 'stereo', :instrument_id => 'drums'} } before(:each) do @@ -74,14 +74,7 @@ describe Track do it "updates a single track using .id to correlate" do track.id.should_not be_nil connection.tracks.length.should == 1 - begin - ActiveRecord::Base.record_timestamps = false - track.updated_at = 1.days.ago - track.save! - ensure - # very important to turn it back; it'll break all tests otherwise - ActiveRecord::Base.record_timestamps = true - end + set_updated_at(track, 1.days.ago) tracks = Track.sync(connection.client_id, [{:id => track.id, :client_track_id => 'client_guid_new', :sound => 'mono', :instrument_id => 'drums'}]) tracks.length.should == 1 found = tracks[0] @@ -105,14 +98,7 @@ describe Track do it "does not touch updated_at when nothing changes" do track.id.should_not be_nil connection.tracks.length.should == 1 - begin - ActiveRecord::Base.record_timestamps = false - track.updated_at = 1.days.ago - track.save! - ensure - # very important to turn it back; it'll break all tests otherwise - ActiveRecord::Base.record_timestamps = true - end + set_updated_at(track, 1.days.ago) tracks = Track.sync(connection.client_id, [{:id => track.id, :client_track_id => track.client_track_id, :sound => track.sound, :instrument_id => track.instrument_id}]) tracks.length.should == 1 found = tracks[0] diff --git a/ruby/spec/jam_ruby/models/user_spec.rb b/ruby/spec/jam_ruby/models/user_spec.rb index 744aef38d..0e0396fa3 100644 --- a/ruby/spec/jam_ruby/models/user_spec.rb +++ b/ruby/spec/jam_ruby/models/user_spec.rb @@ -21,6 +21,7 @@ describe User do it { should respond_to(:admin) } it { should respond_to(:valid_password?) } it { should respond_to(:can_invite) } + it { should respond_to(:mods) } it { should be_valid } it { should_not be_admin } @@ -69,6 +70,24 @@ describe User do it { should_not be_valid } end + describe "when mods is null" do + before { @user.mods = nil } + it { should be_valid } + end + + describe "when mods is empty" do + before { @user.mods = 'nil' } + it { should_not be_valid } + end + + + describe "when mods is json object" do + before { @user.mods = '{"key":"value"}' } + it { should be_valid } + end + + + describe "first or last name cant have profanity" do it "should not let the first name have profanity" do @user.first_name = "fuck you" @@ -118,6 +137,7 @@ describe User do it "should be saved as all lower-case" do + pending @user.email = mixed_case_email @user.save! @user.reload.email.should == mixed_case_email.downcase @@ -428,6 +448,29 @@ describe User do end + + describe "mods" do + it "should allow update of JSON" do + @user.mods = {some_field: 5}.to_json + @user.save! + end + + it "should return heartbeart interval" do + @user.heartbeat_interval_client.should be_nil + @user.mods = {heartbeat_interval_client: 5}.to_json + @user.save! + @user = User.find(@user.id) # necessary because mods_json is cached in the model + @user.heartbeat_interval_client.should == 5 + end + + it "should return connection_expire_time" do + @user.connection_expire_time_client.should be_nil + @user.mods = {connection_expire_time_client: 5}.to_json + @user.save! + @user = User.find(@user.id) # necessary because mods_json is cached in the model + @user.connection_expire_time_client.should == 5 + end + end =begin describe "update avatar" do diff --git a/ruby/spec/jam_ruby/mq_router_spec.rb b/ruby/spec/jam_ruby/mq_router_spec.rb index b9224bde2..3122af502 100644 --- a/ruby/spec/jam_ruby/mq_router_spec.rb +++ b/ruby/spec/jam_ruby/mq_router_spec.rb @@ -11,7 +11,7 @@ describe MQRouter do user1 = FactoryGirl.create(:user) # in the jam session user2 = FactoryGirl.create(:user) # in the jam session - music_session = FactoryGirl.create(:music_session, :creator => user1) + music_session = FactoryGirl.create(:active_music_session, :creator => user1) music_session_member1 = FactoryGirl.create(:connection, :user => user1, :music_session => music_session, :ip_address => "1.1.1.1", :client_id => "1") music_session_member2 = FactoryGirl.create(:connection, :user => user2, :music_session => music_session, :ip_address => "2.2.2.2", :client_id => "2") @@ -37,7 +37,7 @@ describe MQRouter do user1 = FactoryGirl.create(:user) # in the jam session user2 = FactoryGirl.create(:user) # in the jam session - music_session = FactoryGirl.create(:music_session, :creator => user1) + music_session = FactoryGirl.create(:active_music_session, :creator => user1) music_session_member1 = FactoryGirl.create(:connection, :user => user1, :music_session => music_session, :ip_address => "1.1.1.1", :client_id => "1") music_session_member2 = FactoryGirl.create(:connection, :user => user2, :music_session => music_session, :ip_address => "2.2.2.2", :client_id => "2") diff --git a/ruby/spec/jam_ruby/resque/audiomixer_spec.rb b/ruby/spec/jam_ruby/resque/audiomixer_spec.rb index 94918bc9e..95e7b136f 100644 --- a/ruby/spec/jam_ruby/resque/audiomixer_spec.rb +++ b/ruby/spec/jam_ruby/resque/audiomixer_spec.rb @@ -154,7 +154,7 @@ describe AudioMixer do @connection = FactoryGirl.create(:connection, :user => @user) @instrument = FactoryGirl.create(:instrument, :description => 'a great instrument') @track = FactoryGirl.create(:track, :connection => @connection, :instrument => @instrument) - @music_session = FactoryGirl.create(:music_session, :creator => @user, :musician_access => true) + @music_session = FactoryGirl.create(:active_music_session, :creator => @user, :musician_access => true) # @music_session.connections << @connection @music_session.save @connection.join_the_session(@music_session, true, nil) diff --git a/ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb b/ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb index 45226452c..587080fe5 100644 --- a/ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb +++ b/ruby/spec/jam_ruby/resque/google_analytics_event_spec.rb @@ -13,7 +13,7 @@ describe GoogleAnalyticsEvent do ResqueSpec.reset! user = FactoryGirl.create(:user) band = FactoryGirl.create(:band) - music_session = FactoryGirl.create(:music_session, + music_session = FactoryGirl.create(:active_music_session, :creator => user, :musician_access => true, :band => band) @@ -34,7 +34,7 @@ describe GoogleAnalyticsEvent do band.users << user band.users << user1 band.reload - music_session = FactoryGirl.create(:music_session, :creator => user, + music_session = FactoryGirl.create(:active_music_session, :creator => user, :musician_access => true, :band => band) expect(band.band_musicians.count).to eq(2) expect(band.did_real_session).to eq(false) @@ -73,7 +73,7 @@ describe GoogleAnalyticsEvent do end it 'reports size increment' do user = FactoryGirl.create(:user) - music_session = FactoryGirl.create(:music_session, + music_session = FactoryGirl.create(:active_music_session, :creator => user, :musician_access => true) connection = FactoryGirl.create(:connection, :user => user, @@ -88,7 +88,7 @@ describe GoogleAnalyticsEvent do it 'reports duration' do user = FactoryGirl.create(:user) JamRuby::GoogleAnalyticsEvent::SessionDurationTracker.should have_schedule_size_of(0) - music_session = FactoryGirl.create(:music_session, + music_session = FactoryGirl.create(:active_music_session, :creator => user, :musician_access => true) GoogleAnalyticsEvent::SessionDurationTracker.should have_schedule_size_of(1) diff --git a/ruby/spec/jam_ruby/resque/icecast_config_worker_spec.rb b/ruby/spec/jam_ruby/resque/icecast_config_worker_spec.rb index e10c5f847..c93363a94 100644 --- a/ruby/spec/jam_ruby/resque/icecast_config_worker_spec.rb +++ b/ruby/spec/jam_ruby/resque/icecast_config_worker_spec.rb @@ -136,14 +136,7 @@ describe IcecastConfigWriter do pending "failing on build server" server.touch - begin - ActiveRecord::Base.record_timestamps = false - server.updated_at = Time.now.ago(APP_CONFIG.icecast_max_missing_check + 1) - server.save! - ensure - # very important to turn it back; it'll break all tests otherwise - ActiveRecord::Base.record_timestamps = true - end + set_updated_at(server, Time.now.ago(APP_CONFIG.icecast_max_missing_check + 1)) # should enqueue 1 job IcecastConfigWriter.queue_jobs_needing_retry diff --git a/ruby/spec/support/utilities.rb b/ruby/spec/support/utilities.rb index 0f78e56c4..23b37a3e6 100644 --- a/ruby/spec/support/utilities.rb +++ b/ruby/spec/support/utilities.rb @@ -122,6 +122,18 @@ def run_tests? type ENV["RUN_#{type}_TESTS"] == "1" || ENV[type] == "1" || ENV['ALL_TESTS'] == "1" end +# you have go out of your way to update 'updated_at ' +def set_updated_at(resource, time) + begin + ActiveRecord::Base.record_timestamps = false + resource.updated_at = time + resource.save!(validate: false) + ensure + # very important to turn it back; it'll break all tests otherwise + ActiveRecord::Base.record_timestamps = true + end +end + def wipe_s3_test_bucket # don't bother if the user isn't doing AWS tests if run_tests? :aws diff --git a/update b/update index a5495fcb3..969b747c0 100755 --- a/update +++ b/update @@ -17,7 +17,7 @@ pushd pb popd echo "" -echo "updating database" +echo "updating ruby" echo "" pushd ruby bundle update diff --git a/web/Gemfile b/web/Gemfile index 2595343ce..1ff24b15f 100644 --- a/web/Gemfile +++ b/web/Gemfile @@ -5,7 +5,7 @@ unless ENV["LOCAL_DEV"] == "1" end # Look for $WORKSPACE, otherwise use "workspace" as dev path. -devenv = ENV["BUILD_NUMBER"].nil? # Jenkins sets a build number environment variable +devenv = ENV["BUILD_NUMBER"].nil? || ENV["TEST_WWW"] == "1" if devenv gem 'jam_db', :path=> "../db/target/ruby_package" diff --git a/web/app/assets/images/content/icon_thumbsdown_big_off.png b/web/app/assets/images/content/icon_thumbsdown_big_off.png new file mode 100644 index 000000000..bd7e0261b Binary files /dev/null and b/web/app/assets/images/content/icon_thumbsdown_big_off.png differ diff --git a/web/app/assets/images/content/icon_thumbsdown_big_on.png b/web/app/assets/images/content/icon_thumbsdown_big_on.png new file mode 100644 index 000000000..69438d595 Binary files /dev/null and b/web/app/assets/images/content/icon_thumbsdown_big_on.png differ diff --git a/web/app/assets/images/content/icon_thumbsup_big_off.png b/web/app/assets/images/content/icon_thumbsup_big_off.png new file mode 100644 index 000000000..00ecd4f00 Binary files /dev/null and b/web/app/assets/images/content/icon_thumbsup_big_off.png differ diff --git a/web/app/assets/images/content/icon_thumbsup_big_on.png b/web/app/assets/images/content/icon_thumbsup_big_on.png new file mode 100644 index 000000000..39bcc1a8a Binary files /dev/null and b/web/app/assets/images/content/icon_thumbsup_big_on.png differ diff --git a/web/app/assets/images/isps/ping-icon.jpg b/web/app/assets/images/isps/ping-icon.jpg new file mode 100644 index 000000000..b1585aa94 Binary files /dev/null and b/web/app/assets/images/isps/ping-icon.jpg differ diff --git a/web/app/assets/javascripts/AAA_Log.js b/web/app/assets/javascripts/AAA_Log.js index cd16f22b6..bdd411e38 100644 --- a/web/app/assets/javascripts/AAA_Log.js +++ b/web/app/assets/javascripts/AAA_Log.js @@ -15,6 +15,12 @@ 'exception', 'table' ]; + var log_methods = { + 'log':null, 'debug':null, 'info':null, 'warn':null, 'error':null, 'assert':null, 'trace':null, 'exception':null + } + + var logCache = []; + if ('undefined' === typeof(context.console)) { context.console = {}; $.each(console_methods, function(index, value) { @@ -27,23 +33,39 @@ context.console.debug = function() { console.log(arguments); } } - context.JK.logger = context.console; + // http://tobyho.com/2012/07/27/taking-over-console-log/ + function takeOverConsole(){ + var console = window.console + if (!console) return + function intercept(method){ + var original = console[method] + console[method] = function(){ - // JW - some code to tone down logging. Uncomment the following, and - // then do your logging to logger.dbg - and it will be the only thing output. - // TODO - find a way to wrap this up so that debug logs can stay in, but this - // class can provide a way to enable/disable certain namespaces of logs. - /* - var fakeLogger = {}; - $.each(console_methods, function(index, value) { - fakeLogger[value] = $.noop; - }); - fakeLogger.dbg = function(m) { - context.console.debug(m); - }; - context.JK.logger = fakeLogger; - */ + logCache.push([method].concat(arguments)); + if(logCache.length > 50) { + // keep the cache size 50 or lower + logCache.pop(); + } + if (original.apply){ + // Do this for normal browsers + original.apply(console, arguments) + }else{ + // Do this for IE + var message = Array.prototype.slice.apply(arguments).join(' ') + original(message) + } + } + } + var methods = ['log', 'warn', 'error'] + for (var i = 0; i < methods.length; i++) + intercept(methods[i]) + } + + takeOverConsole(); + + context.JK.logger = context.console; + context.JK.logger.logCache = logCache; })(window, jQuery); \ No newline at end of file diff --git a/web/app/assets/javascripts/JamServer.js b/web/app/assets/javascripts/JamServer.js index d1126adf1..8f3a6c2ef 100644 --- a/web/app/assets/javascripts/JamServer.js +++ b/web/app/assets/javascripts/JamServer.js @@ -14,15 +14,22 @@ context.JK.JamServer = function (app) { + // uniquely identify the websocket connection + var channelId = null; + var clientType = null; + // heartbeat var heartbeatInterval = null; var heartbeatMS = null; - var heartbeatMissedMS = 10000; // if 5 seconds go by and we haven't seen a heartbeat ack, get upset + var connection_expire_time = null; + var lastHeartbeatSentTime = null; var lastHeartbeatAckTime = null; var lastHeartbeatFound = false; + var lastDisconnectedReason = null; var heartbeatAckCheckInterval = null; var notificationLastSeenAt = undefined; var notificationLastSeen = undefined; + var clientClosedConnection = false; // reconnection logic var connectDeferred = null; @@ -53,17 +60,23 @@ server.connected = false; + function heartbeatStateReset() { + lastHeartbeatSentTime = null; + lastHeartbeatAckTime = null; + lastHeartbeatFound = false; + } + // if activeElementVotes is null, then we are assuming this is the initial connect sequence function initiateReconnect(activeElementVotes, in_error) { var initialConnect = !!activeElementVotes; freezeInteraction = activeElementVotes && ((activeElementVotes.dialog && activeElementVotes.dialog.freezeInteraction === true) || (activeElementVotes.screen && activeElementVotes.screen.freezeInteraction === true)); - if(!initialConnect) { + if (!initialConnect) { context.JK.CurrentSessionModel.onWebsocketDisconnected(in_error); } - if(in_error) { + if (in_error) { reconnectAttempt = 0; $currentDisplay = renderDisconnected(); beginReconnectPeriod(); @@ -87,7 +100,7 @@ if (server.connected) { server.connected = false; - if(app.clientUpdating) { + if (app.clientUpdating) { // we don't want to do a 'cover the whole screen' dialog // because the client update is already showing. return; @@ -126,8 +139,9 @@ // check if the server is still sending heartbeat acks back down // this logic equates to 'if we have not received a heartbeat within heartbeatMissedMS, then get upset - if (new Date().getTime() - lastHeartbeatAckTime.getTime() > heartbeatMissedMS) { - logger.error("no heartbeat ack received from server after ", heartbeatMissedMS, " seconds . giving up on socket connection"); + if (new Date().getTime() - lastHeartbeatAckTime.getTime() > connection_expire_time) { + logger.error("no heartbeat ack received from server after ", connection_expire_time, " seconds . giving up on socket connection"); + lastDisconnectedReason = 'NO_HEARTBEAT_ACK'; context.JK.JamServer.close(true); } else { @@ -140,6 +154,16 @@ var message = context.JK.MessageFactory.heartbeat(notificationLastSeen, notificationLastSeenAt); notificationLastSeenAt = undefined; notificationLastSeen = undefined; + // for debugging purposes, see if the last time we've sent a heartbeat is way off (500ms) of the target interval + var now = new Date(); + + if (lastHeartbeatSentTime) { + var drift = new Date().getTime() - lastHeartbeatSentTime.getTime() - heartbeatMS; + if (drift > 500) { + logger.error("significant drift between heartbeats: " + drift + 'ms beyond target interval') + } + } + lastHeartbeatSentTime = now; context.JK.JamServer.send(message); lastHeartbeatFound = false; } @@ -147,11 +171,13 @@ function loggedIn(header, payload) { - if(!connectTimeout) { + if (!connectTimeout) { clearTimeout(connectTimeout); connectTimeout = null; } + heartbeatStateReset(); + app.clientId = payload.client_id; // tell the backend that we have logged in @@ -159,12 +185,13 @@ $.cookie('client_id', payload.client_id); + heartbeatMS = payload.heartbeat_interval * 1000; - logger.debug("jamkazam.js.loggedIn(): clientId now " + app.clientId + "; Setting up heartbeat every " + heartbeatMS + " MS"); + connection_expire_time = payload.connection_expire_time * 1000; + logger.debug("jamkazam.js.loggedIn(): clientId=" + app.clientId + ", heartbeat=" + payload.heartbeat_interval + "s, expire_time=" + payload.connection_expire_time + 's'); heartbeatInterval = context.setInterval(_heartbeat, heartbeatMS); heartbeatAckCheckInterval = context.setInterval(_heartbeatAckCheck, 1000); lastHeartbeatAckTime = new Date(new Date().getTime() + heartbeatMS); // add a little forgiveness to server for initial heartbeat - connectDeferred.resolve(); app.activeElementEvent('afterConnect', payload); @@ -209,10 +236,10 @@ function internetUp() { var start = new Date().getTime(); server.connect() - .done(function() { + .done(function () { guardAgainstRapidTransition(start, performReconnect); }) - .fail(function() { + .fail(function () { guardAgainstRapidTransition(start, closedOnReconnectAttempt); }); } @@ -224,18 +251,37 @@ function performReconnect() { - if($currentDisplay.is('.no-websocket-connection')) { - $currentDisplay.hide(); + if(!clientClosedConnection) { + lastDisconnectedReason = 'WEBSOCKET_CLOSED_REMOTELY' + clientClosedConnection = false; + } + else if(!lastDisconnectedReason) { + // let's have at least some sort of type, however generci + lastDisconnectedReason = 'WEBSOCKET_CLOSED_LOCALLY' + } + + rest.createDiagnostic({ + type: lastDisconnectedReason, + data: {logs: logger.logCache, client_type: clientType, client_id: server.clientID, channel_id: channelId} + }) + .always(function() { + if ($currentDisplay.is('.no-websocket-connection')) { + // this path is the 'not in session path'; so there is nothing else to do + $currentDisplay.hide(); + + // TODO: tell certain elements that we've reconnected + } + else { + // this path is the 'in session' path, where we actually reload the page + context.JK.CurrentSessionModel.leaveCurrentSession() + .always(function () { + window.location.reload(); + }); + } + server.reconnecting = false; + }); + - // TODO: tell certain elements that we've reconnected - } - else { - context.JK.CurrentSessionModel.leaveCurrentSession() - .always(function() { - window.location.reload(); - }); - } - server.reconnecting = false; } function buildOptions() { @@ -245,14 +291,14 @@ function renderDisconnected() { var content = null; - if(freezeInteraction) { + if (freezeInteraction) { var template = $templateDisconnected.html(); var templateHtml = $(context.JK.fillTemplate(template, buildOptions())); templateHtml.find('.reconnect-countdown').html(formatDelaySecs(reconnectDelaySecs())); content = context.JK.Banner.show({ - html : templateHtml, + html: templateHtml, type: 'reconnect' - }) ; + }); } else { var $inSituContent = $(context._.template($templateServerConnection.html(), buildOptions(), { variable: 'data' })); @@ -267,7 +313,7 @@ } function formatDelaySecs(secs) { - return $('' + secs + ' ' + (secs == 1 ? ' second.s' : 'seconds.') + ''); + return $('' + secs + ' ' + (secs == 1 ? ' second.s' : 'seconds.') + ''); } function setCountdown($parent) { @@ -281,7 +327,7 @@ function renderReconnecting() { $currentDisplay.find('.reconnect-progress-msg').text('Attempting to reconnect...') - if($currentDisplay.is('.no-websocket-connection')) { + if ($currentDisplay.is('.no-websocket-connection')) { $currentDisplay.find('.disconnected-reconnect').removeClass('reconnect-enabled').addClass('reconnect-disabled'); } else { @@ -299,7 +345,7 @@ var now = new Date().getTime(); if ((now - start) < 1500) { - setTimeout(function() { + setTimeout(function () { nextStep(); }, 1500 - (now - start)) } @@ -315,12 +361,12 @@ renderReconnecting(); rest.serverHealthCheck() - .done(function() { + .done(function () { guardAgainstRapidTransition(start, internetUp); }) - .fail(function(xhr, textStatus, errorThrown) { + .fail(function (xhr, textStatus, errorThrown) { - if(xhr && xhr.status >= 100) { + if (xhr && xhr.status >= 100) { // we could connect to the server, and it's alive guardAgainstRapidTransition(start, internetUp); } @@ -333,7 +379,7 @@ } function clearReconnectTimers() { - if(countdownInterval) { + if (countdownInterval) { clearInterval(countdownInterval); countdownInterval = null; } @@ -341,8 +387,8 @@ function beginReconnectPeriod() { // allow user to force reconnect - $currentDisplay.find('a.disconnected-reconnect').unbind('click').click(function() { - if($(this).is('.button-orange') || $(this).is('.reconnect-enabled')) { + $currentDisplay.find('a.disconnected-reconnect').unbind('click').click(function () { + if ($(this).is('.button-orange') || $(this).is('.reconnect-enabled')) { clearReconnectTimers(); attemptReconnect(); } @@ -353,9 +399,9 @@ reconnectDueTime = reconnectingWaitPeriodStart + reconnectDelaySecs() * 1000; // update count down timer periodically - countdownInterval = setInterval(function() { + countdownInterval = setInterval(function () { var now = new Date().getTime(); - if(now > reconnectDueTime) { + if (now > reconnectDueTime) { clearReconnectTimers(); attemptReconnect(); } @@ -404,9 +450,14 @@ }; server.connect = function () { + if(!clientType) { + clientType = context.JK.clientType(); + } connectDeferred = new $.Deferred(); - logger.log("server.connect"); - var uri = context.JK.websocket_gateway_uri; // Set in index.html.erb. + channelId = context.JK.generateUUID(); // create a new channel ID for every websocket connection + logger.log("connecting websocket, channel_id: " + channelId); + + var uri = context.JK.websocket_gateway_uri + '?channel_id=' + channelId; // Set in index.html.erb. //var uri = context.gon.websocket_gateway_uri; // Leaving here for now, as we're looking for a better solution. server.socket = new context.WebSocket(uri); @@ -414,9 +465,10 @@ server.socket.onmessage = server.onMessage; server.socket.onclose = server.onClose; - connectTimeout = setTimeout(function() { + connectTimeout = setTimeout(function () { connectTimeout = null; - if(connectDeferred.state() === 'pending') { + if (connectDeferred.state() === 'pending') { + server.close(true); connectDeferred.reject(); } }, 4000); @@ -427,6 +479,7 @@ server.close = function (in_error) { logger.log("closing websocket"); + clientClosedConnection = true; server.socket.close(); closedCleanup(in_error); @@ -435,7 +488,7 @@ server.rememberLogin = function () { var token, loginMessage; token = $.cookie("remember_token"); - var clientType = context.jamClient.IsNativeClient() ? 'client' : 'browser'; + loginMessage = msg_factory.login_with_token(token, null, clientType); server.send(loginMessage); }; @@ -471,10 +524,11 @@ } }; + // onClose is called if either client or server closes connection server.onClose = function () { logger.log("Socket to server closed."); - if(connectDeferred.state() === "pending") { + if (connectDeferred.state() === "pending") { connectDeferred.reject(); } @@ -521,19 +575,19 @@ //console.timeEnd('sendP2PMessage'); }; - server.updateNotificationSeen = function(notificationId, notificationCreatedAt) { + server.updateNotificationSeen = function (notificationId, notificationCreatedAt) { var time = new Date(notificationCreatedAt); - if(!notificationCreatedAt) { + if (!notificationCreatedAt) { throw 'invalid value passed to updateNotificationSeen' } - if(!notificationLastSeenAt) { + if (!notificationLastSeenAt) { notificationLastSeenAt = notificationCreatedAt; notificationLastSeen = notificationId; logger.debug("updated notificationLastSeenAt with: " + notificationCreatedAt); } - else if(time.getTime() > new Date(notificationLastSeenAt).getTime()) { + else if (time.getTime() > new Date(notificationLastSeenAt).getTime()) { notificationLastSeenAt = notificationCreatedAt; notificationLastSeen = notificationId; logger.debug("updated notificationLastSeenAt with: " + notificationCreatedAt); @@ -573,6 +627,7 @@ } function initialize() { + registerLoginAck(); registerHeartbeatAck(); registerSocketClosed(); @@ -584,12 +639,24 @@ $templateServerConnection = $('#template-server-connection'); $templateDisconnected = $('#template-disconnected'); - if($inSituBanner.length != 1) { throw "found wrong number of .server-connection: " + $inSituBanner.length; } - if($inSituBannerHolder.length != 1) { throw "found wrong number of .no-websocket-connection: " + $inSituBannerHolder.length; } - if($messageContents.length != 1) { throw "found wrong number of .message-contents: " + $messageContents.length; } - if($dialog.length != 1) { throw "found wrong number of #banner: " + $dialog.length; } - if($templateServerConnection.length != 1) { throw "found wrong number of #template-server-connection: " + $templateServerConnection.length; } - if($templateDisconnected.length != 1) { throw "found wrong number of #template-disconnected: " + $templateDisconnected.length; } + if ($inSituBanner.length != 1) { + throw "found wrong number of .server-connection: " + $inSituBanner.length; + } + if ($inSituBannerHolder.length != 1) { + throw "found wrong number of .no-websocket-connection: " + $inSituBannerHolder.length; + } + if ($messageContents.length != 1) { + throw "found wrong number of .message-contents: " + $messageContents.length; + } + if ($dialog.length != 1) { + throw "found wrong number of #banner: " + $dialog.length; + } + if ($templateServerConnection.length != 1) { + throw "found wrong number of #template-server-connection: " + $templateServerConnection.length; + } + if ($templateDisconnected.length != 1) { + throw "found wrong number of #template-disconnected: " + $templateDisconnected.length; + } } this.initialize = initialize; diff --git a/web/app/assets/javascripts/configureTrack.js b/web/app/assets/javascripts/configureTrack.js index d3bfb0eb2..344c99302 100644 --- a/web/app/assets/javascripts/configureTrack.js +++ b/web/app/assets/javascripts/configureTrack.js @@ -7,18 +7,8 @@ var logger = context.JK.logger; var myTrackCount; - var ASSIGNMENT = { - CHAT: -2, - OUTPUT: -1, - UNASSIGNED: 0, - TRACK1: 1, - TRACK2: 2 - }; - - var VOICE_CHAT = { - NO_CHAT: "0", - CHAT: "1" - }; + var ASSIGNMENT = context.JK.ASSIGNMENT; + var VOICE_CHAT = context.JK.VOICE_CHAT; var instrument_array = []; diff --git a/web/app/assets/javascripts/createSession.js.erb b/web/app/assets/javascripts/createSession.js.erb index 687527bbd..8532d162c 100644 --- a/web/app/assets/javascripts/createSession.js.erb +++ b/web/app/assets/javascripts/createSession.js.erb @@ -179,51 +179,42 @@ // Defaulting to 1st instrument in profile always at the moment. data.tracks = tracks; - var jsonData = JSON.stringify(data); - $('#btn-create-session').addClass('button-disabled'); $('#btn-create-session').bind('click', false); - var url = "/api/sessions"; - $.ajax({ - type: "POST", - dataType: "json", - contentType: 'application/json', - url: url, - processData:false, - data: jsonData, - success: function(response) { - var newSessionId = response.id; - var invitationCount = inviteMusiciansUtil.createInvitations(newSessionId, function() { - context.location = '/client#/session/' + newSessionId; - }); - // Re-loading the session settings will cause the form to reset with the right stuff in it. - // This is an extra xhr call, but it keeps things to a single codepath - loadSessionSettings(); - $('#btn-create-session').removeClass('button-disabled'); - $('#btn-create-session').unbind('click', false); + rest.legacyCreateSession(data) + .done(function(response) { + var newSessionId = response.id; + var invitationCount = inviteMusiciansUtil.createInvitations(newSessionId, function() { + context.location = '/client#/session/' + newSessionId; + }); + // Re-loading the session settings will cause the form to reset with the right stuff in it. + // This is an extra xhr call, but it keeps things to a single codepath + loadSessionSettings(); + $('#btn-create-session').removeClass('button-disabled'); + $('#btn-create-session').unbind('click', false); - context.JK.GA.trackSessionCount(data.musician_access, data.fan_access, invitationCount); - - context.JK.GA.trackSessionMusicians(context.JK.GA.SessionCreationTypes.create); - }, - error: function(jqXHR) { - var handled = false; - if(jqXHR.status = 422) { - var response = JSON.parse(jqXHR.responseText); - if(response["errors"] && response["errors"]["tracks"] && response["errors"]["tracks"][0] == "Please select at least one track") { - app.notifyAlert("No Inputs Configured", $('You will need to reconfigure your audio device.')); - handled = true; - } - } - if(!handled) { - app.notifyServerError(jqXHR, "Unable to Create Session"); - } - $('#btn-create-session').removeClass('button-disabled'); - $('#btn-create-session').unbind('click', false); + context.JK.GA.trackSessionCount(data.musician_access, data.fan_access, invitationCount); + context.JK.GA.trackSessionMusicians(context.JK.GA.SessionCreationTypes.create); + }) + .fail(function(jqXHR) { + var handled = false; + if(jqXHR.status = 422) { + var response = JSON.parse(jqXHR.responseText); + if(response["errors"] && response["errors"]["tracks"] && response["errors"]["tracks"][0] == "Please select at least one track") { + app.notifyAlert("No Inputs Configured", $('You will need to reconfigure your audio device.')); + handled = true; + } } - }); + if(!handled) { + app.notifyServerError(jqXHR, "Unable to Create Session"); + } + $('#btn-create-session').removeClass('button-disabled'); + $('#btn-create-session').unbind('click', false); + + }) + return false; } diff --git a/web/app/assets/javascripts/feed.js b/web/app/assets/javascripts/feed.js index c32871db9..da4c107cb 100644 --- a/web/app/assets/javascripts/feed.js +++ b/web/app/assets/javascripts/feed.js @@ -286,7 +286,7 @@ function renderFeeds(feeds) { $.each(feeds.entries, function(i, feed) { - if(feed.type == 'music_session_history') { + if(feed.type == 'music_session') { var options = { feed_item: feed, status_class: feed['is_over?'] ? 'ended' : 'inprogress', diff --git a/web/app/assets/javascripts/ga.js b/web/app/assets/javascripts/ga.js index 11a96eed6..a2d30f72b 100644 --- a/web/app/assets/javascripts/ga.js +++ b/web/app/assets/javascripts/ga.js @@ -19,6 +19,11 @@ join : "Join" }; + var sessionQualityTypes = { + good : "Good", + poor : "Poor" + }; + var invitationTypes = { email : "Email", facebook : "Facebook", @@ -83,6 +88,7 @@ audioTest : "AudioTest", sessionCount : "SessionCount", sessionMusicians : "SessionMusicians", + sessionQuality : "SessionQuality", invite : "Invite", findSession : "FindSession", friendConnect : "Connect", @@ -174,6 +180,11 @@ context.ga('send', 'event', categories.sessionMusicians, joinOrCreate); } + function trackSessionQuality(goodOrPoor) { + assertOneOf(goodOrPoor, sessionQualityTypes); + context.ga('send', 'event', categories.sessionQuality, goodOrPoor); + } + function trackServiceInvitations(invitationType, numInvited) { assertOneOf(invitationType, invitationTypes); assertNumber(numInvited); @@ -271,6 +282,7 @@ var GA = {}; GA.Categories = categories; GA.SessionCreationTypes = sessionCreationTypes; + GA.SessionQualityTypes = sessionQualityTypes; GA.InvitationTypes = invitationTypes; GA.FriendConnectTypes = friendConnectTypes; GA.RecordingActions = recordingActions; @@ -281,6 +293,7 @@ GA.trackFTUECompletion = trackFTUECompletion; GA.trackSessionCount = trackSessionCount; GA.trackSessionMusicians = trackSessionMusicians; + GA.trackSessionQuality = trackSessionQuality; GA.trackServiceInvitations = trackServiceInvitations; GA.trackFindSessions = trackFindSessions; GA.virtualPageView = virtualPageView; diff --git a/web/app/assets/javascripts/gear_wizard.js b/web/app/assets/javascripts/gear_wizard.js index 48ac0c668..9012c91be 100644 --- a/web/app/assets/javascripts/gear_wizard.js +++ b/web/app/assets/javascripts/gear_wizard.js @@ -6,6 +6,8 @@ context.JK = context.JK || {}; context.JK.GearWizard = function (app) { + var ASSIGNMENT = context.JK.ASSIGNMENT; + var VOICE_CHAT = context.JK.VOICE_CHAT; var $dialog = null; var $wizardSteps = null; @@ -20,11 +22,11 @@ // populated by loadDevices var deviceInformation = null; - var musicInputPorts = null; - var musicOutputPorts = null; + var musicPorts = null; - // SELECT DEVICE STATE - var validScore = false; + + var validLatencyScore = false; + var validIOScore = false; // SELECT TRACKS STATE @@ -46,7 +48,7 @@ display: 'MacOSX Built-In', videoURL: undefined }, - MACOSX_interface: { + MacOSX_interface: { display: 'MacOSX external interface', videoURL: undefined }, @@ -86,13 +88,19 @@ var $bufferIn = $currentWizardStep.find('.select-buffer-in'); var $bufferOut = $currentWizardStep.find('.select-buffer-out'); var $frameSize = $currentWizardStep.find('.select-frame-size'); - var $inputPorts = $currentWizardStep.find('.input-ports'); - var $outputPorts = $currentWizardStep.find('.output-ports'); + var $inputChannels = $currentWizardStep.find('.input-ports'); + var $outputChannels = $currentWizardStep.find('.output-ports'); var $scoreReport = $currentWizardStep.find('.results'); + var $latencyScoreSection = $scoreReport.find('.latency-score-section'); var $latencyScore = $scoreReport.find('.latency-score'); + var $ioScoreSection = $scoreReport.find('.io-score-section'); var $ioRateScore = $scoreReport.find('.io-rate-score'); var $ioVarScore = $scoreReport.find('.io-var-score'); + var $ioCountdown = $scoreReport.find('.io-countdown'); + var $ioCountdownSecs = $scoreReport.find('.io-countdown .secs'); var $nextButton = $ftueButtons.find('.btn-next'); + var $asioControlPanelBtn = $currentWizardStep.find('.asio-settings-btn'); + var $resyncBtn = $currentWizardStep.find('resync-btn') // should return one of: // * MacOSX_builtin @@ -126,22 +134,31 @@ } } - function loadDevices() { - var devices = context.jamClient.FTUEGetDevices(false); + + var oldDevices = context.jamClient.FTUEGetDevices(false); + var devices = context.jamClient.FTUEGetAudioDevices(); + console.log("oldDevices: " + JSON.stringify(oldDevices)); + console.log("devices: " + JSON.stringify(devices)); var loadedDevices = {}; // augment these devices by determining their type - context._.each(devices, function (displayName, deviceId) { + context._.each(devices.devices, function (device) { + + if(device.name == "JamKazam Virtual Monitor") { + return; + } + var deviceInfo = {}; - deviceInfo.id = deviceId; - deviceInfo.type = determineDeviceType(deviceId, displayName); + deviceInfo.id = device.guid; + deviceInfo.type = determineDeviceType(device.guid, device.display_name); + console.log("deviceInfo.type: " + deviceInfo.type) deviceInfo.displayType = audioDeviceBehavior[deviceInfo.type].display; - deviceInfo.displayName = displayName; + deviceInfo.displayName = device.display_name; - loadedDevices[deviceId] = deviceInfo; + loadedDevices[device.guid] = deviceInfo; logger.debug("loaded device: ", deviceInfo); }) @@ -179,7 +196,7 @@ function initializeNextButtonState() { $nextButton.removeClass('button-orange button-grey'); - if (validScore) $nextButton.addClass('button-orange'); + if (validLatencyScore) $nextButton.addClass('button-orange'); else $nextButton.addClass('button-grey'); } @@ -218,71 +235,66 @@ context.JK.dropdown($bufferOut); } - // finds out if the $port argument is from a different port pair than what's currently selected - function isNewlySelectedPair($port) { - var portId = $port.attr('data-id'); - // get all inputs currently selected except this one - var $selectedInputs = $inputPorts.find('input[type="checkbox"]:checked').filter('[data-id="' + portId + '"]'); - console.log("$selectedInputs", $selectedInputs); - var isNewlySelected = true; - context._.each($selectedInputs, function($current) { - var testPairInfo = $($current).data('pair'); + // reloads the backend's channel state for the currently selected audio devices, + // and update's the UI accordingly + function initializeChannels() { + musicPorts = jamClient.FTUEGetChannels(); + console.log("musicPorts: %o", JSON.stringify(musicPorts)); - context._.each(testPairInfo.ports, function(port) { - // if we can find the newly selected item in this pair, then it's not a different pair... - if(port.id == portId) { - isNewlySelected = false; - return false; // break loop - } - }); + initializeInputPorts(musicPorts); + initializeOutputPorts(musicPorts); + } - if(isNewlySelected) return false; // break loop + // during this phase of the FTUE, we have to assign selected input channels + // to tracks. The user, however, does not have a way to indicate which channel + // goes to which track (that's not until the next step of the wizard). + // so, we just auto-generate a valid assignment + function newInputAssignment() { + var assigned = 0; + context._.each(musicPorts.inputs, function(inputChannel) { + if(isChannelAssigned(inputChannel)) { + assigned += 1; + } }); - return isNewlySelected; + var newAssignment = Math.floor(assigned / 2) + 1; + return newAssignment; } - // set checkbox state for all items in the pair - function setCheckedForAllInPair($portBox, pairInfo, checked, signalBackend) { - context._.each(pairInfo.ports, function(port) { - var portId = port.id; - var $input = $portBox.find('input[type="checkbox"][data-id="' + portId + '"]'); - if($input.is(':checked') != checked) { - if(checked) { - $input.iCheck('check').attr('checked', 'checked'); - //context.jamClient.FTUESetMusicInput2($input.id); - } - else { - $input.iCheck('uncheck').removeAttr('checked'); - //context.jamClient.FTUEUnsetMusicInput2($input.id); - } - } - }) - } - - function inputPortChanged() { + function inputChannelChanged() { if(iCheckIgnore) return; var $checkbox = $(this); - var portId = $checkbox.data('data-id'); - var inputPortChecked = $checkbox.is(':checked'); - console.log('inputPortChecked: ' + inputPortChecked); + var channelId = $checkbox.attr('data-id'); + var isChecked = $checkbox.is(':checked'); - if(inputPortChecked) { - if(isNewlySelectedPair($checkbox)) { - setCheckedForAllInPair($inputPorts, $checkbox.data('pair'), true, true); - } - else { - //context.jamClient.FTUESetMusicInput2($input.id); - } + if(isChecked) { + var newAssignment = newInputAssignment(); + logger.debug("assigning input channel %o to track: %o", channelId, newAssignment); + context.jamClient.TrackSetAssignment(channelId, true, newAssignment); } else { - // context.jamClient.FTUEUnsetMusicInput2($input.id);; + logger.debug("unassigning input channel %o", channelId); + context.jamClient.TrackSetAssignment(channelId, true, ASSIGNMENT.UNASSIGNED); + // unassigning creates a hole in our auto-assigned tracks. reassign them all to keep it consistent + var $assignedInputs = $inputChannels.find('input[type="checkbox"]:checked'); + var assigned = 0; + context._.each($assignedInputs, function(assignedInput) { + var $assignedInput = $(assignedInput); + var assignedChannelId = $assignedInput.attr('data-id'); + var newAssignment = Math.floor(assigned / 2) + 1; + logger.debug("re-assigning input channel %o to track: %o", assignedChannelId, newAssignment); + context.jamClient.TrackSetAssignment(assignedChannelId, true, newAssignment); + assigned += 1; + }); } + + initializeChannels(); } - // should be called in a ifChanged callback if you want to cancel. bleh. + // should be called in a ifChanged callback if you want to cancel. + // you have to use this instead of 'return false' like a typical input 'change' event. function cancelICheckChange($checkbox) { iCheckIgnore = true; var checked = $checkbox.is(':checked'); @@ -293,58 +305,64 @@ }, 1); } - function outputPortChanged() { + function outputChannelChanged() { if(iCheckIgnore) return; - var $checkbox = $(this); - var portId = $checkbox.data('data-id'); - var outputPortChecked = $checkbox.is(':checked'); - console.log('outputPortChecked: ' + outputPortChecked); + var channelId = $checkbox.attr('data-id'); + var isChecked = $checkbox.is(':checked'); - if(outputPortChecked) { - var $selectedInputs = $outputPorts.find('input[type="checkbox"]:checked').filter('[data-id="' + portId + '"]'); - $selectedInputs.iCheck('uncheck').removeAttr('checked'); - var pairInfo = $checkbox.data('pair'); - setCheckedForAllInPair($outputPorts, pairInfo, true, false); - console.log("Setting music output"); - context.jamClient.FTUESetMusicOutput(pairInfo.ports.map(function(i) {return i.id}).join(PROFILE_DEV_SEP_TOKEN)); - } - else { - context.JK.Banner.showAlert('You must have at least one output pair selected.'); + // don't allow more than 2 output channels selected at once + if($outputChannels.find('input[type="checkbox"]:checked').length > 2) { + context.JK.Banner.showAlert('You can only have a maximum of 2 output ports selected.'); // can't allow uncheck of last output cancelICheckChange($checkbox); + return; } + + if(isChecked) { + logger.debug("assigning output channel %o", channelId); + context.jamClient.TrackSetAssignment(channelId, true, ASSIGNMENT.OUTPUT); + } + else { + logger.debug("unassigning output channel %o", channelId); + context.jamClient.TrackSetAssignment(channelId, true, ASSIGNMENT.UNASSIGNED); + } + + initializeChannels(); } - function initializeInputPorts(inputPorts) { - context._.each(inputPorts, function(inputPairs) { - // there is no guarantee that a pair has two items. - context._.each(inputPairs.ports, function(inputInPair) { - var inputPort = $(context._.template($templateAudioPort.html(), inputInPair, { variable: 'data' })); - var $checkbox = inputPort.find('input'); - $checkbox.data('pair', inputPairs); // so when it's selected, we can see what other ports, if any, are in the same pair - context.JK.checkbox($checkbox); - $checkbox.on('ifChanged', inputPortChanged); - $inputPorts.append(inputPort); - }); + // checks if it's an assigned OUTPUT or ASSIGNED CHAT + function isChannelAssigned(channel) { + return channel.assignment == ASSIGNMENT.CHAT || channel.assignment == ASSIGNMENT.OUTPUT || channel.assignment > 0; + } + + function initializeInputPorts(musicPorts) { + $inputChannels.empty(); + var inputPorts = musicPorts.inputs; + context._.each(inputPorts, function(inputChannel) { + var $inputChannel = $(context._.template($templateAudioPort.html(), inputChannel, { variable: 'data' })); + var $checkbox = $inputChannel.find('input'); + if(isChannelAssigned(inputChannel)) { + $checkbox.attr('checked', 'checked'); + } + context.JK.checkbox($checkbox); + $checkbox.on('ifChanged', inputChannelChanged); + $inputChannels.append($inputChannel); }); } - function initializeOutputPorts(outputPorts) { - var first = true; - context._.each(outputPorts, function(outputPairs) { - context._.each(outputPairs.ports, function(outputInPair) { - var outputPort = $(context._.template($templateAudioPort.html(), outputInPair, { variable: 'data' })); - var $checkbox = outputPort.find('input'); - $checkbox.data('pair', outputPairs); // so when it's selected, we can see what other ports, if any, are in the same pair - context.JK.checkbox($checkbox); - $checkbox.on('ifChanged', outputPortChanged); - $outputPorts.append(outputPort); - }); - if(first) { - first = false; - setCheckedForAllInPair($outputPorts, outputPairs, true, false); + function initializeOutputPorts(musicPorts) { + $outputChannels.empty(); + var outputChannels = musicPorts.outputs; + context._.each(outputChannels, function(outputChannel) { + var $outputPort = $(context._.template($templateAudioPort.html(), outputChannel, { variable: 'data' })); + var $checkbox = $outputPort.find('input'); + if(isChannelAssigned(outputChannel)) { + $checkbox.attr('checked', 'checked'); } + context.JK.checkbox($checkbox); + $checkbox.on('ifChanged', outputChannelChanged); + $outputChannels.append($outputPort); }); } @@ -364,11 +382,11 @@ } function clearInputPorts() { - $inputPorts.empty(); + $inputChannels.empty(); } function clearOutputPorts() { - $outputPorts.empty(); + $outputChannels.empty(); } function resetScoreReport() { @@ -377,6 +395,27 @@ $latencyScore.empty(); } + function renderLatencyScore(latencyValue, latencyClass) { + if(latencyValue) { + $latencyScore.text(latencyValue + ' ms'); + } + else { + $latencyScore.text(''); + } + $latencyScoreSection.removeClass('good acceptable bad unknown starting').addClass(latencyClass); + } + + // std deviation is the worst value between in/out + // media is the worst value between in/out + // io is the value returned by the backend, which has more info + // ioClass is the pre-computed rollup class describing the result in simple terms of 'good', 'acceptable', bad' + function renderIOScore(std, median, ioData, ioClass) { + $ioRateScore.text(median ? median : ''); + $ioVarScore.text(std ? std : ''); + $ioScoreSection.removeClass('good acceptable bad unknown starting skip').addClass(ioClass); + // TODO: show help bubble of all data in IO data + } + function updateScoreReport(latencyResult) { var latencyClass = "neutral"; var latencyValue = 'N/A'; @@ -387,37 +426,69 @@ if (latencyValue <= 10) { latencyClass = "good"; validLatency = true; - } else if (latency.latency <= 20) { + } else if (latencyValue <= 20) { latencyClass = "acceptable"; validLatency = true; } else { latencyClass = "bad"; } } + else { + latencyClass = 'unknown'; + } - validScore = validLatency; // validScore may become based on IO variance too + validLatencyScore = validLatency; - $latencyScore.html(latencyValue + ' ms'); + renderLatencyScore(latencyValue, latencyClass); } function audioInputDeviceUnselected() { - validScore = false; + validLatencyScore = false; initializeNextButtonState(); resetFrameBuffers(); clearInputPorts(); } function renderScoringStarted() { - validScore = false; + validLatencyScore = false; initializeNextButtonState(); resetScoreReport(); + freezeAudioInteraction(); + renderLatencyScore(null, 'starting'); } function renderScoringStopped() { initializeNextButtonState(); + unfreezeAudioInteraction(); } + function freezeAudioInteraction() { + $audioInput.attr("disabled", "disabled").easyDropDown('disable'); + $audioOutput.attr("disabled", "disabled").easyDropDown('disable'); + $frameSize.attr("disabled", "disabled").easyDropDown('disable'); + $bufferIn.attr("disabled", "disabled").easyDropDown('disable'); + $bufferOut.attr("disabled", "disabled").easyDropDown('disable'); + $asioControlPanelBtn.on("click", false); + $resyncBtn.on('click', false); + iCheckIgnore = true; + $inputChannels.find('input[type="checkbox"]').iCheck('disable'); + $outputChannels.find('input[type="checkbox"]').iCheck('disable'); + } + + function unfreezeAudioInteraction() { + $audioInput.removeAttr("disabled").easyDropDown('enable'); + $audioOutput.removeAttr("disabled").easyDropDown('enable'); + $frameSize.removeAttr("disabled").easyDropDown('enable'); + $bufferIn.removeAttr("disabled").easyDropDown('enable'); + $bufferOut.removeAttr("disabled").easyDropDown('enable'); + $asioControlPanelBtn.off("click", false); + $resyncBtn.off('click', false); + $inputChannels.find('input[type="checkbox"]').iCheck('enable'); + $outputChannels.find('input[type="checkbox"]').iCheck('enable'); + iCheckIgnore = false; + } + // Given a latency structure, update the view. function newFtueUpdateLatencyView(latency) { var $report = $('.ftue-new .latency .report'); @@ -506,44 +577,52 @@ }); } - function initializeAudioInputChanged() { - $audioInput.unbind('change').change(function (evt) { + function renderIOScoringStarted(secondsLeft) { + $ioCountdownSecs.text(secondsLeft); + $ioCountdown.show(); + } - var audioDeviceId = selectedAudioInput(); - if (!audioDeviceId) { + function renderIOScoringStopped() { + $ioCountdown.hide(); + } + + function renderIOCountdown(secondsLeft) { + $ioCountdownSecs.text(secondsLeft); + } + + function attemptScore() { + var audioInputDeviceId = selectedAudioInput(); + var audioOutputDeviceId = selectedAudioOutput(); + if (!audioInputDeviceId) { audioInputDeviceUnselected(); return false; } - var audioDevice = findDevice(selectedAudioInput()); - if (!audioDevice) { - context.JK.alertSupportedNeeded('Unable to find device information for: ' + audioDeviceId); + var audioInputDevice = findDevice(audioInputDeviceId); + if (!audioInputDevice) { + context.JK.alertSupportedNeeded('Unable to find information for input device: ' + audioInputDeviceId); return false; } + if(!audioOutputDeviceId) { + audioOutputDeviceId = audioInputDeviceId; + } + var audioOutputDevice = findDevice(audioOutputDeviceId); + if (!audioInputDevice) { + context.JK.alertSupportedNeeded('Unable to find information for output device: ' + audioOutputDeviceId); + return false; + } + jamClient.FTUESetInputMusicDevice(audioInputDeviceId); + jamClient.FTUESetOutputMusicDevice(audioOutputDeviceId); - renderScoringStarted(); + initializeChannels(); - jamClient.FTUESetMusicDevice(audioDeviceId); - - // enumerate input and output ports - musicInputPorts = jamClient.FTUEGetMusicInputs2(); - console.log(JSON.stringify(musicInputPorts)); - // [{"inputs":[{"id":"i~5~Built-in Microph~0~0","name":"Built-in Microph - Left"},{"id":"i~5~Built-in Microph~1~0","name":"Built-in Microph - Right"}]}] - musicOutputPorts = jamClient.FTUEGetMusicOutputs2(); - console.log(JSON.stringify(musicOutputPorts)); - // [{"outputs":[{"id":"o~5~Built-in Output~0~0","name":"Built-in Output - Left"},{"id":"o~5~Built-in Output~1~0","name":"Built-in Output - Right"}]}] - - - initializeInputPorts(musicInputPorts); - initializeOutputPorts(musicOutputPorts); - - - jamClient.FTUESetInputLatency(selectedAudioInput()); - jamClient.FTUESetOutputLatency(selectedAudioOutput()); + jamClient.FTUESetInputLatency(selectedBufferIn()); + jamClient.FTUESetOutputLatency(selectedBufferOut()); jamClient.FTUESetFrameSize(selectedFramesize()); + renderScoringStarted(); logger.debug("Calling FTUESave(false)"); jamClient.FTUESave(false); @@ -551,8 +630,67 @@ console.log("FTUEGetExpectedLatency: %o", latency); updateScoreReport(latency); - renderScoringStopped(); - }); + + // if there was a valid latency score, go on to the next step + if(validLatencyScore) { + renderIOScore(null, null, null, 'starting'); + var testTimeSeconds = 10; // allow 10 seconds for IO to establish itself + context.jamClient.FTUEStartIoPerfTest(); + renderIOScoringStarted(testTimeSeconds); + renderIOCountdown(testTimeSeconds); + var interval = setInterval(function() { + testTimeSeconds -= 1; + renderIOCountdown(testTimeSeconds); + if(testTimeSeconds == 0) { + clearInterval(interval); + renderIOScoringStopped(); + var io = context.jamClient.FTUEGetIoPerfData(); + + console.log("io: ", io); + + // take the higher variance, which is apparently actually std dev + var std = io.in_var > io.out_var ? io.in_var : io.out_var; + std = Math.round(std * 100) / 100; + // take the furthest-off-from-target io rate + var median = Math.abs(io.in_median - io.in_target ) > Math.abs(io.out_median - io.out_target ) ? [io.in_median, io.in_target] : [io.out_median, io.out_target]; + var medianTarget = median[1]; + median = Math.round(median[0]); + + var stdIOClass = 'bad'; + if(std <= 0.50) { + stdIOClass = 'good'; + } + else if(std <= 1.00) { + stdIOClass = 'acceptable'; + } + + var medianIOClass = 'bad'; + if(Math.abs(median - medianTarget) <= 1) { + medianIOClass = 'good'; + } + else if(Math.abs(median - medianTarget) <= 2) { + medianIOClass = 'acceptable'; + } + + // now base the overall IO score based on both values. + renderIOScore(std, median, io, ioClass); + + // lie for now until IO questions finalize + validIOScore = true; + + renderScoringStopped(); + } + }, 1000); + } + else { + renderIOScore(null, null, null, 'skip'); + renderScoringStopped(); + } + + } + + function initializeAudioInputChanged() { + $audioInput.unbind('change').change(attemptScore); } function initializeAudioOutputChanged() { @@ -677,7 +815,31 @@ $currentWizardStep = null; } + // checks if we already have a profile called 'FTUE...'; if not, create one. if so, re-use it. + function findOrCreateFTUEProfile() { + var profileName = context.jamClient.FTUEGetMusicProfileName(); + + logger.debug("current profile name: " + profileName); + + if(profileName && profileName.indexOf('FTUE') == 0) { + + } + else { + var newProfileName = 'FTUEAttempt-' + new Date().getTime().toString(); + logger.debug("setting FTUE-prefixed profile name to: " + newProfileName); + context.jamClient.FTUESetMusicProfileName(newProfileName); + } + + var profileName = context.jamClient.FTUEGetMusicProfileName(); + + logger.debug("name on exit: " + profileName); + + } + function beforeShow(args) { + context.jamClient.FTUECancel(); + findOrCreateFTUEProfile(); + step = args.d1; if (!step) step = 0; step = parseInt(step); @@ -689,7 +851,7 @@ } function afterHide() { - + context.jamClient.FTUECancel(); } function back() { diff --git a/web/app/assets/javascripts/globals.js b/web/app/assets/javascripts/globals.js index 49341c071..7c3a37ec3 100644 --- a/web/app/assets/javascripts/globals.js +++ b/web/app/assets/javascripts/globals.js @@ -14,7 +14,20 @@ UNIX: "Unix" }; - // TODO: store these client_id values in instruments table, or store + context.JK.ASSIGNMENT = { + CHAT: -2, + OUTPUT: -1, + UNASSIGNED: 0, + TRACK1: 1, + TRACK2: 2 + }; + + context.JK.VOICE_CHAT = { + NO_CHAT: "0", + CHAT: "1" + }; + + // TODO: store these client_id values in instruments table, or store // server_id as the client_id to prevent maintenance nightmares. As it's // set up now, we will have to deploy each time we add new instruments. context.JK.server_to_client_instrument_map = { diff --git a/web/app/assets/javascripts/hoverBand.js b/web/app/assets/javascripts/hoverBand.js index 7f85e0dbc..3d5115341 100644 --- a/web/app/assets/javascripts/hoverBand.js +++ b/web/app/assets/javascripts/hoverBand.js @@ -32,8 +32,8 @@ $.each(response.musicians, function(index, val) { var instrumentHtml = ''; - musicianHtml += ''; - musicianHtml += '' + val.name + ''; + musicianHtml += ''; + musicianHtml += '<' + val.name + ''; instrumentHtml = '
'; if (val.instruments) { // @FIXME: edge case for Test user that has no instruments? @@ -75,6 +75,8 @@ }); $(hoverSelector).append('

Band Detail

' + bandHtml); + + context.JK.bindProfileClickEvents(hoverSelector); configureActionButtons(response); }) .fail(function(xhr) { diff --git a/web/app/assets/javascripts/hoverFan.js b/web/app/assets/javascripts/hoverFan.js index a68f65204..7bf13ec1c 100644 --- a/web/app/assets/javascripts/hoverFan.js +++ b/web/app/assets/javascripts/hoverFan.js @@ -36,19 +36,21 @@ followingHtml += ''; } - var avatarUrl, profilePath; + var avatarUrl, attrId, type; if (val.type === "band") { avatarUrl = context.JK.resolveBandAvatarUrl(val.photo_url); - profilePath = "bandProfile"; + attrId = "band-id"; + type = "band"; } else { avatarUrl = context.JK.resolveAvatarUrl(val.photo_url); - profilePath = "profile"; + attrId = "user-id"; + type = "musician"; } - followingHtml += ''; - followingHtml += '' + val.name + ''; + followingHtml += ''; + followingHtml += '' + val.name + ''; if (index % 2 > 0) { followingHtml += ''; @@ -76,6 +78,8 @@ }); $(hoverSelector).append('

Fan Detail

' + fanHtml); + + context.JK.bindProfileClickEvents(hoverSelector); configureActionButtons(response); }) .fail(function(xhr) { diff --git a/web/app/assets/javascripts/hoverMusician.js b/web/app/assets/javascripts/hoverMusician.js index 360bc664d..985da502b 100644 --- a/web/app/assets/javascripts/hoverMusician.js +++ b/web/app/assets/javascripts/hoverMusician.js @@ -42,19 +42,21 @@ followingHtml += ''; } - var avatarUrl, profilePath; + var avatarUrl, attrId, type; if (val.type === "band") { avatarUrl = context.JK.resolveBandAvatarUrl(val.photo_url); - profilePath = "bandProfile"; + attrId = "band-id"; + type = "band"; } else { avatarUrl = context.JK.resolveAvatarUrl(val.photo_url); - profilePath = "profile"; + attrId = "user-id"; + type = "musician"; } - followingHtml += ''; - followingHtml += '' + val.name + ''; + followingHtml += ''; + followingHtml += '' + val.name + ''; if (index % 2 > 0) { followingHtml += ''; @@ -101,6 +103,8 @@ }); $(hoverSelector).append('

Musician Detail

' + musicianHtml); + + context.JK.bindProfileClickEvents(hoverSelector); configureActionButtons(response); }) .fail(function(xhr) { diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js index aa59990f7..3334d5c38 100644 --- a/web/app/assets/javascripts/jam_rest.js +++ b/web/app/assets/javascripts/jam_rest.js @@ -34,6 +34,30 @@ }); } + function legacyCreateSession(options) { + return $.ajax({ + type: "POST", + dataType: "json", + contentType: 'application/json', + url: "/api/sessions/legacy", + processData:false, + data: JSON.stringify(options)}); + } + + function legacyJoinSession(options) { + var sessionId = options["session_id"]; + delete options["session_id"]; + + return $.ajax({ + type: "POST", + dataType: "json", + contentType: 'application/json', + url: "/api/sessions/" + sessionId + "/participants/legacy", + data: JSON.stringify(options), + processData:false + }); + } + function findSessions(query) { return $.ajax({ type: "GET", @@ -109,7 +133,7 @@ function addPlayablePlay(playableId, playableType, claimedRecordingId, userId) { if (playableType == 'JamRuby::Recording') { context.JK.GA.trackRecordingPlay(); - } else if (playableType == 'JamRuby::MusicSessionHistory') { + } else if (playableType == 'JamRuby::MusicSession') { context.JK.GA.trackSessionPlay(); } return $.ajax({ @@ -961,6 +985,16 @@ url: '/api/sessions/' + musciSessionId + '/chats?' + $.param(options), dataType: "json", contentType: 'application/json' + }) + }; + + function createDiagnostic(options) { + return $.ajax({ + type: "POST", + url: '/api/diagnostics', + dataType: "json", + contentType: 'application/json', + data: JSON.stringify(options) }); } @@ -970,6 +1004,8 @@ // Expose publics this.initialize = initialize; + this.legacyCreateSession = legacyCreateSession; + this.legacyJoinSession = legacyJoinSession; this.getUserDetail = getUserDetail; this.getCities = getCities; this.getRegions = getRegions; @@ -1048,6 +1084,7 @@ this.getNotifications = getNotifications; this.createChatMessage = createChatMessage; this.getChatMessages = getChatMessages; + this.createDiagnostic = createDiagnostic; return this; }; diff --git a/web/app/assets/javascripts/joinSession.js b/web/app/assets/javascripts/joinSession.js index 25c3e3300..6de6ee73c 100644 --- a/web/app/assets/javascripts/joinSession.js +++ b/web/app/assets/javascripts/joinSession.js @@ -35,7 +35,7 @@ as_musician: true, tracks: [ track ] }; - var url = "/api/sessions/" + session_id + "/participants"; + var url = "/api/sessions/" + session_id + "/participants/legacy"; $.ajax({ type: "POST", dataType: "json", diff --git a/web/app/assets/javascripts/jquery.listenbroadcast.js b/web/app/assets/javascripts/jquery.listenbroadcast.js index fbbc090c7..5468a3236 100644 --- a/web/app/assets/javascripts/jquery.listenbroadcast.js +++ b/web/app/assets/javascripts/jquery.listenbroadcast.js @@ -92,7 +92,7 @@ // keep this after transition, because any transition clears this timer waitForBufferingTimeout = setTimeout(noBuffer, WAIT_FOR_BUFFER_TIMEOUT); - rest.addPlayablePlay(musicSessionId, 'JamRuby::MusicSessionHistory', null, context.JK.currentUserId); + rest.addPlayablePlay(musicSessionId, 'JamRuby::MusicSession', null, context.JK.currentUserId); } }) } diff --git a/web/app/assets/javascripts/rateSessionDialog.js b/web/app/assets/javascripts/rateSessionDialog.js new file mode 100644 index 000000000..644585bb5 --- /dev/null +++ b/web/app/assets/javascripts/rateSessionDialog.js @@ -0,0 +1,123 @@ +(function(context,$) { + + "use strict"; + + context.JK = context.JK || {}; + context.JK.RateSessionDialog = function(app) { + var logger = context.JK.logger; + var dialogId = 'rate-session-dialog'; + var $scopeSelector = "[layout-id='rate-session-dialog']"; + var clientId = context.JK.JamServer.clientID; + + function reset() { + clientId = context.JK.JamServer.clientID; + $('#btn-rate-session-up', $scopeSelector).removeClass('selected'); + $('#btn-rate-session-down', $scopeSelector).removeClass('selected'); + $('#txt-rate-session-comment',"[layout-id='rate-session-dialog']").val(''); + } + + function showDialog() { + if (clientId) { + reset(); + $.ajax({ + type: "GET", + url: "/api/participant_histories/"+clientId + }).done(function (response) { + if (response && + response.hasOwnProperty('should_rate_session') && + true==response['should_rate_session']) { + app.layout.showDialog(dialogId); + } + }); + return true; + } + return false; + } + + function closeDialog() { + app.layout.closeDialog(dialogId); + } + + function getRating() { + if ($('#btn-rate-session-down', $scopeSelector).hasClass('selected')) { + return -1; + } else if ($('#btn-rate-session-up', $scopeSelector).hasClass('selected')) { + return 1; + } + return 0; + } + + function getComment() { + return $('#txt-rate-session-comment',"[layout-id='rate-session-dialog']").val(); + } + + function events() { + $('#btn-rate-session-cancel', $scopeSelector).click(function(evt) { + closeDialog(); + }); + $('#btn-rate-session-up', $scopeSelector).click(function(evt) { + if ($(this).hasClass('selected')) { + $(this).removeClass('selected') + } else { + $(this).addClass('selected'); + } + if ($('#btn-rate-session-down').hasClass('selected')) { + $('#btn-rate-session-down').removeClass('selected') + } + return false; + }); + $('#btn-rate-session-down', $scopeSelector).click(function(evt) { + if ($(this).hasClass('selected')) { + $(this).removeClass('selected') + } else { + $(this).addClass('selected'); + } + if ($('#btn-rate-session-up').hasClass('selected')) { + $('#btn-rate-session-up').removeClass('selected') + } + return false; + }); + $('#btn-rate-session-send', $scopeSelector).click(function(evt) { + var rr = getRating(), cc = getComment(); + if (0 == rr && 0 == cc.length) { + closeDialog(); + return false; + } + var url = "/api/participant_histories/"+clientId+"/rating"; + $.ajax({ + type: "POST", + url: url, + data: { rating: getRating(), comment: getComment() } + }).done(function (response) { + var qq = getRating(); + if (0 < qq) { + context.JK.GA.trackSessionQuality(context.JK.GA.SessionQualityTypes.good); + } else if (0 > qq){ + context.JK.GA.trackSessionQuality(context.JK.GA.SessionQualityTypes.poor); + } + closeDialog(); + }); + return false; + }); + } + + function beforeShow(data) { + // confirm user should see dialog + } + + function afterShow(data) { + } + + function initialize() { + var dialogBindings = { + 'beforeShow' : beforeShow, + 'afterShow' : afterShow + }; + app.bindDialog(dialogId, dialogBindings); + events(); + } + + this.initialize = initialize; + this.showDialog = showDialog; + }; +})(window,jQuery); diff --git a/web/app/assets/javascripts/session.js b/web/app/assets/javascripts/session.js index e667e27c0..8f9da6756 100644 --- a/web/app/assets/javascripts/session.js +++ b/web/app/assets/javascripts/session.js @@ -31,6 +31,7 @@ var playbackControls = null; var promptLeave = false; var backendMixerAlertThrottleTimer = null; + var rateSessionDialog = null; var rest = context.JK.Rest(); @@ -1305,15 +1306,27 @@ } } + function bailOut() { + promptLeave = false; + context.window.location = '/client#/home'; + } + function sessionLeave(evt) { evt.preventDefault(); - - promptLeave = false; - context.window.location = '/client#/home'; - + rateSession(); + bailOut(); return false; } + function rateSession() { + if (rateSessionDialog === null) { + rateSessionDialog = new context.JK.RateSessionDialog(context.JK.app); + rateSessionDialog.initialize(); + } + rateSessionDialog.showDialog(); + return true; + } + function sessionResync(evt) { evt.preventDefault(); var response = context.jamClient.SessionAudioResync(); diff --git a/web/app/assets/javascripts/sessionModel.js b/web/app/assets/javascripts/sessionModel.js index abdabe683..791503e06 100644 --- a/web/app/assets/javascripts/sessionModel.js +++ b/web/app/assets/javascripts/sessionModel.js @@ -361,17 +361,11 @@ client_id: clientId, ip_address: server.publicIP, as_musician: true, - tracks: tracks + tracks: tracks, + session_id: sessionId }; - var url = "/api/sessions/" + sessionId + "/participants"; - return $.ajax({ - type: "POST", - dataType: "json", - contentType: 'application/json', - url: url, - data: JSON.stringify(data), - processData:false - }); + + return rest.legacyJoinSession(data); } function leaveSessionRest(sessionId) { diff --git a/web/app/assets/javascripts/utils.js b/web/app/assets/javascripts/utils.js index 0140c1b9f..fd689608d 100644 --- a/web/app/assets/javascripts/utils.js +++ b/web/app/assets/javascripts/utils.js @@ -607,6 +607,9 @@ doneYet(); }; + context.JK.clientType = function () { + return context.jamClient.IsNativeClient() ? 'client' : 'browser'; + } /** * Returns 'MacOSX' if the os appears to be macintosh, * 'Win32' if the os appears to be windows, diff --git a/web/app/assets/javascripts/web/recordings.js b/web/app/assets/javascripts/web/recordings.js index 1fecac65b..c83b42007 100644 --- a/web/app/assets/javascripts/web/recordings.js +++ b/web/app/assets/javascripts/web/recordings.js @@ -94,6 +94,7 @@ $(".landing-comment-scroller").prepend(commentHtml); } + context.JK.bindProfileClickEvents(); context.JK.bindHoverEvents(); } diff --git a/web/app/assets/javascripts/web/sessions.js b/web/app/assets/javascripts/web/sessions.js index c31bd62c7..66fadcbfb 100644 --- a/web/app/assets/javascripts/web/sessions.js +++ b/web/app/assets/javascripts/web/sessions.js @@ -49,6 +49,7 @@ $(".landing-comment-scroller").prepend(commentHtml); } + context.JK.bindProfileClickEvents(); context.JK.bindHoverEvents(); } @@ -129,7 +130,6 @@ $("#btnLike").click(like); $playButton.trigger('click'); - pollForUpdates(musicSessionId); } diff --git a/web/app/assets/stylesheets/client/gearWizard.css.scss b/web/app/assets/stylesheets/client/gearWizard.css.scss index f2c635714..aa2ac4bf6 100644 --- a/web/app/assets/stylesheets/client/gearWizard.css.scss +++ b/web/app/assets/stylesheets/client/gearWizard.css.scss @@ -207,6 +207,39 @@ font-size:15px; @include border_box_sizing; height:64px; + + &.good { + background-color:#72a43b; + } + &.acceptable { + background-color:#cc9900; + } + &.bad, &.skip { + background-color:#660000; + } + &.unknown { + background-color:#999; + } + } + + .io-countdown { + display:none; + padding-left:19px; + position:relative; + + .secs { + position:absolute; + width:19px; + left:0; + } + } + + .io-skip-msg { + display:none; + + .scoring-section.skip & { + display:inline; + } } } diff --git a/web/app/assets/stylesheets/client/session.css.scss b/web/app/assets/stylesheets/client/session.css.scss index cd389c60f..387352b0d 100644 --- a/web/app/assets/stylesheets/client/session.css.scss +++ b/web/app/assets/stylesheets/client/session.css.scss @@ -727,4 +727,26 @@ table.vu td { #update-session-invite-musicians { margin: 10px; -} \ No newline at end of file +} + +.rate-thumbsup { + width:64px; + height:64px; + display:inline-block; + background-image:url('/assets/content/icon_thumbsup_big_off.png'); +} + +.rate-thumbsup.selected { + background-image:url('/assets/content/icon_thumbsup_big_on.png'); +} + +.rate-thumbsdown { + width:64px; + height:64px; + display:inline-block; + background-image:url('/assets/content/icon_thumbsdown_big_off.png'); +} + +.rate-thumbsdown.selected { + background-image:url('/assets/content/icon_thumbsdown_big_on.png'); +} diff --git a/web/app/controllers/api_chats_controller.rb b/web/app/controllers/api_chats_controller.rb index 9f1e1d491..4a58fd164 100644 --- a/web/app/controllers/api_chats_controller.rb +++ b/web/app/controllers/api_chats_controller.rb @@ -25,7 +25,7 @@ class ApiChatsController < ApiController end def check_session - @music_session = MusicSession.find(params[:music_session]) + @music_session = ActiveMusicSession.find(params[:music_session]) if @music_session.nil? raise ArgumentError, 'specified session not found' end diff --git a/web/app/controllers/api_diagnostics_controller.rb b/web/app/controllers/api_diagnostics_controller.rb new file mode 100644 index 000000000..ac8a17ce9 --- /dev/null +++ b/web/app/controllers/api_diagnostics_controller.rb @@ -0,0 +1,16 @@ +class ApiDiagnosticsController < ApiController + + before_filter :api_signed_in_user + respond_to :json + + def create + @diagnostic = Diagnostic.new + @diagnostic.type = params[:type] + @diagnostic.data = params[:data].to_json if params[:data] + @diagnostic.user = current_user + @diagnostic.creator = Diagnostic::CLIENT + @diagnostic.save + + respond_with_model(@diagnostic, new: true) + end +end diff --git a/web/app/controllers/api_music_sessions_controller.rb b/web/app/controllers/api_music_sessions_controller.rb index a7d96b3d1..1f5929394 100644 --- a/web/app/controllers/api_music_sessions_controller.rb +++ b/web/app/controllers/api_music_sessions_controller.rb @@ -22,7 +22,7 @@ class ApiMusicSessionsController < ApiController # Importantly, friends_only and my_bands_only are ORed not ANDed. So, if you specify both as true, you'll get more # results than if only one or the other is true, not fewer. if either is true you won't see the "everything else" # sessions. - @music_sessions = MusicSession.index(current_user, + @music_sessions = ActiveMusicSession.index(current_user, participants: params[:participants], genres: params[:genres], friends_only: params[:friends_only], @@ -48,7 +48,7 @@ class ApiMusicSessionsController < ApiController # Importantly, friends_only and my_bands_only are ORed not ANDed. So, if you specify both as true, you'll get more # results than if only one or the other is true, not fewer. if either is true you won't see the "everything else" # sessions. - @music_sessions = MusicSession.nindex(current_user, + @music_sessions = ActiveMusicSession.nindex(current_user, client_id: params[:client_id], participants: params[:participants], genres: params[:genres], @@ -60,39 +60,68 @@ class ApiMusicSessionsController < ApiController limit: params[:limit]) end - def create + def create_legacy client_id = params[:client_id] if client_id.nil? raise JamArgumentError, "client_id must be specified" end - if !params[:intellectual_property] + unless params[:intellectual_property] raise JamArgumentError, "You must agree to the intellectual property terms" end band = Band.find(params[:band]) unless params[:band].nil? - @music_session = MusicSessionManager.new.create( - current_user, - client_id, - params[:description], - params[:musician_access], - params[:approval_required], - params[:fan_chat], - params[:fan_access], - band, - params[:genres], - params[:tracks], - params[:legal_terms]) + # creating the MusicSession right here was added as part of the scheduled sessions changes + # Why? The new order of things is to always have a MusicSession before a ActiveMusicSession + # So, we have to make MusicSession, and pass in it's .id to MusicSessionManager.new.create() + # so that the ActiveMusicSession can have the same .id as the MusicSession + history = MusicSession.new + history.name = params[:description][0..40] + history.description = params[:description] + history.musician_access = params[:musician_access] + history.approval_required = params[:approval_required] + history.fan_chat = params[:fan_chat] + history.fan_access = params[:fan_access] + history.band = band + history.genre_id = (params[:genres].length > 0 ? params[:genres][0] : nil) if params[:genres] + history.legal_terms = params[:legal_terms] + history.language = 'english' + history.legal_policy = 'standard' + history.creator = current_user + history.save - if @music_session.errors.any? - # we have to do this because api_session_detail_url will fail with a bad @music_session + if history.errors.any? + @music_session = history response.status = :unprocessable_entity respond_with @music_session else - respond_with @music_session, responder: ApiResponder, :location => api_session_detail_url(@music_session) + history.reload # to get .id back + @music_session = MusicSessionManager.new.create( + history, + current_user, + client_id, + params[:description], + params[:musician_access], + params[:approval_required], + params[:fan_chat], + params[:fan_access], + band, + params[:genres], + params[:tracks], + params[:legal_terms]) + + if @music_session.errors.any? + response.status = :unprocessable_entity + respond_with @music_session + else + respond_with @music_session, responder: ApiResponder, :location => api_session_detail_url(@music_session) + end end + + + end def show @@ -103,7 +132,7 @@ class ApiMusicSessionsController < ApiController def update @music_session = MusicSessionManager.new.update( - @music_session, + @music_session.music_session, params[:description], params[:genres], params[:musician_access], @@ -124,7 +153,7 @@ class ApiMusicSessionsController < ApiController @connection = Connection.find_by_client_id(params[:id]) end - def participant_create + def participant_create_legacy @connection = MusicSessionManager.new.participant_create( current_user, params[:id], @@ -144,23 +173,34 @@ class ApiMusicSessionsController < ApiController client_id = params[:id] if client_id.present? && client_id != 'undefined' @connection = Connection.find_by_client_id!(client_id) - music_session = MusicSession.find(@connection.music_session_id) + music_session = ActiveMusicSession.find(@connection.music_session_id) MusicSessionManager.new.participant_delete(current_user, @connection, music_session) end respond_with @connection, responder: ApiResponder end def participant_rating - @history = MusicSessionUserHistory.find(params[:id]) - @history.rating = params[:rating] - @history.save + if @history = MusicSessionUserHistory.latest_history(params[:client_id]) + if request.post? + @history.add_rating(params[:rating], params[:comment]) + @history.save - if @history.errors.any? - response.status = :unprocessable_entity - respond_with @history - else - render :json => {}, :status => :ok + if @history.errors.any? + response.status = :unprocessable_entity + respond_with @history + else + if @history.good_rating? && @history.user.first_good_music_session_at.nil? + @history.user.first_good_music_session_at = Time.now + @history.user.save + end + render :json => {}, :status => :ok + end + elsif request.get? + render :json => { :should_rate_session => @history.should_rate_session? }, :status => :ok end + else + render :json => { :message => ValidationMessages::SESSION_NOT_FOUND }, :status => 404 + end end def track_index @@ -231,12 +271,12 @@ class ApiMusicSessionsController < ApiController # example of using curl to access this API: # curl -L -T some_file -X PUT http://localhost:3000/api/sessions/[SESSION_ID]/perf.json?client_id=[CLIENT_ID] - music_session_history = MusicSessionHistory.find(params[:id]) + music_session = MusicSession.find(params[:id]) msuh = MusicSessionUserHistory.find_by_client_id(params[:client_id]) @perfdata = MusicSessionPerfData.new @perfdata.client_id = params[:client_id] - @perfdata.music_session_history = music_session_history + @perfdata.music_session = music_session unless @perfdata.save # we have to do this because api_session_detail_url will fail with a bad @music_session response.status = :unprocessable_entity @@ -329,7 +369,7 @@ class ApiMusicSessionsController < ApiController end def history_show - @history = MusicSessionHistory.find(params[:id]) + @history = MusicSession.find(params[:id]) end def claimed_recording_start @@ -359,7 +399,7 @@ class ApiMusicSessionsController < ApiController private def lookup_session - @music_session = MusicSession.find(params[:id]) + @music_session = ActiveMusicSession.find(params[:id]) end end diff --git a/web/app/controllers/api_recordings_controller.rb b/web/app/controllers/api_recordings_controller.rb index a694d7403..478a3aeee 100644 --- a/web/app/controllers/api_recordings_controller.rb +++ b/web/app/controllers/api_recordings_controller.rb @@ -48,12 +48,10 @@ class ApiRecordingsController < ApiController else render :json => { :message => "download limit surpassed" }, :status => 404 end - - end def start - music_session = MusicSession.find(params[:music_session_id]) + music_session = ActiveMusicSession.find(params[:music_session_id]) raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR unless music_session.users.exists?(current_user) diff --git a/web/app/controllers/api_users_controller.rb b/web/app/controllers/api_users_controller.rb index 97fb849c7..56b408ea5 100644 --- a/web/app/controllers/api_users_controller.rb +++ b/web/app/controllers/api_users_controller.rb @@ -555,14 +555,14 @@ class ApiUsersController < ApiController def share_session provider = params[:provider] music_session_id = params[:music_session] - history = MusicSessionHistory.find(music_session_id) + history = MusicSession.find(music_session_id) if provider == 'facebook' render json: { - description: view_context.description_for_music_session_history(history), - title: view_context.title_for_music_session_history(history, current_user), - photo_url: view_context.facebook_image_for_music_session_history(history), + description: view_context.description_for_music_session(history), + title: view_context.title_for_music_session(history, current_user), + photo_url: view_context.facebook_image_for_music_session(history), url: share_token_url(history.share_token.token), caption: 'www.jamkazam.com' }, status: 200 @@ -570,7 +570,7 @@ class ApiUsersController < ApiController elsif provider == 'twitter' render json: { - message: view_context.title_for_music_session_history(history, current_user) + message: view_context.title_for_music_session(history, current_user) }, status: 200 else diff --git a/web/app/controllers/music_sessions_controller.rb b/web/app/controllers/music_sessions_controller.rb index b40914178..d25749826 100644 --- a/web/app/controllers/music_sessions_controller.rb +++ b/web/app/controllers/music_sessions_controller.rb @@ -3,7 +3,7 @@ class MusicSessionsController < ApplicationController respond_to :html def show - @music_session = MusicSessionHistory.find(params[:id]) + @music_session = MusicSession.find(params[:id]) render :layout => "web" end diff --git a/web/app/controllers/ping_controller.rb b/web/app/controllers/ping_controller.rb index fb1fdea5b..b307de35f 100644 --- a/web/app/controllers/ping_controller.rb +++ b/web/app/controllers/ping_controller.rb @@ -28,4 +28,9 @@ class PingController < ApplicationController render 'pingvz.jnlp', :content_type => JNLP end + def icon + redirect_to '/assets/isps/ping-icon.jpg' + #send_file Rails.root.join("app", "assets", "images", "isps", "ping-icon.jpg"), type: "image/jpg", disposition: "inline" + end + end diff --git a/web/app/controllers/spikes_controller.rb b/web/app/controllers/spikes_controller.rb index 29bc4337b..75cde8a49 100644 --- a/web/app/controllers/spikes_controller.rb +++ b/web/app/controllers/spikes_controller.rb @@ -16,7 +16,7 @@ class SpikesController < ApplicationController end #as_musician = false is the critical search criteria for sessions to list correctly - @music_sessions = MusicSession.index(current_user, as_musician: false) + @music_sessions = ActiveMusicSession.index(current_user, as_musician: false) render :layout => 'web' end diff --git a/web/app/helpers/event_session_helper.rb b/web/app/helpers/event_session_helper.rb index 2e9fb3f1d..71ac4a38b 100644 --- a/web/app/helpers/event_session_helper.rb +++ b/web/app/helpers/event_session_helper.rb @@ -44,7 +44,7 @@ module EventSessionHelper # if so, then we are playing. # if there has been none, we say it's still coming, # if there has been at least one, and it's over, we say session over - query = MusicSessionHistory.where(fan_access: true).where(created_at: (event_session.starts_at - 12.hours)..(event_session.ends_at + 12.hours)) + query = MusicSession.where(fan_access: true).where(created_at: (event_session.starts_at - 12.hours)..(event_session.ends_at + 12.hours)) if event_session.user_id query = query.where(user_id: event_session.user_id) elsif event_session.band_id @@ -62,14 +62,14 @@ module EventSessionHelper state = event_session.pinned_state if event_session.pinned_state if state - music_session_history = fetch_last_session(event_session) + music_session = fetch_last_session(event_session) elsif !state && (event_session.starts_at && event_session.ends_at && (event_session.user_id || event_session.band_id)) - music_session_history = fetch_last_session(event_session) + music_session = fetch_last_session(event_session) - if music_session_history - if music_session_history.session_removed_at + if music_session + if music_session.session_removed_at state = 'over' else state = 'playing' @@ -80,9 +80,9 @@ module EventSessionHelper end if state == 'over' - content_tag(:a, 'SESSION ENDED', href: music_session_history.nil? ? '#' : music_session_detail_path(music_session_history.id), class: 'button-grey') + content_tag(:a, 'SESSION ENDED', href: music_session.nil? ? '#' : music_session_detail_path(music_session.id), class: 'button-grey') elsif state == 'playing' - content_tag(:a, '', href: music_session_detail_path(music_session_history.id), class: 'button-orange') do + content_tag(:a, '', href: music_session_detail_path(music_session.id), class: 'button-orange') do content_tag(:span, image_tag('content/icon_playbutton.png', :width => 20, height: 20, align: 'absmiddle', class:'listen-now-play') + 'LISTEN NOW') end elsif state == 'not_started' diff --git a/web/app/helpers/feeds_helper.rb b/web/app/helpers/feeds_helper.rb index 2929b89e2..1f9f135b6 100644 --- a/web/app/helpers/feeds_helper.rb +++ b/web/app/helpers/feeds_helper.rb @@ -1,42 +1,42 @@ module FeedsHelper - def session_artist_name(music_session_history) - (music_session_history.band.nil? ? nil : music_session_history.band.name) || music_session_history.user.name + def session_artist_name(music_session) + (music_session.band.nil? ? nil : music_session.band.name) || music_session.creator.name end - def session_artist_id(music_session_history) - (music_session_history.band.nil? ? nil : music_session_history.band.id) || music_session_history.user.id + def session_artist_id(music_session) + (music_session.band.nil? ? nil : music_session.band.id) || music_session.creator.id end - def session_artist_hoveraction(music_session_history) - music_session_history.band.nil? ? 'musician' : 'band' + def session_artist_hoveraction(music_session) + music_session.band.nil? ? 'musician' : 'band' end - def session_artist_datakey(music_session_history) - music_session_history.band.nil? ? 'user-id' : 'band-id' + def session_artist_datakey(music_session) + music_session.band.nil? ? 'user-id' : 'band-id' end - def session_avatar(music_session_history) - image_tag resolve_avatarables(music_session_history.band, music_session_history.user) + def session_avatar(music_session) + image_tag resolve_avatarables(music_session.band, music_session.creator) end - def session_duration_value(music_session_history) - if music_session_history.session_removed_at.nil? - Time.now - music_session_history.created_at + def session_duration_value(music_session) + if music_session.session_removed_at.nil? + Time.now - music_session.created_at else - music_session_history.session_removed_at - music_session_history.created_at + music_session.session_removed_at - music_session.created_at end end - def session_duration(music_session_history, options={}) - duration(session_duration_value(music_session_history), options) + def session_duration(music_session, options={}) + duration(session_duration_value(music_session), options) end - def session_text(music_session_history) - if music_session_history.is_over? + def session_text(music_session) + if music_session.is_over? 'SESSION ENDED' else - if music_session_history.fan_access - if music_session_history.music_session && music_session_history.music_session.mount + if music_session.fan_access + if music_session.active_music_session && music_session.active_music_session.mount 'SESSION IN PROGRESS' else 'BROADCASTING OFFLINE' # if you see this in development, you need to set up icecast. If you see this in production, it's not healthy @@ -47,15 +47,13 @@ module FeedsHelper end end - def session_description(music_session_history) - music_session_history.description + def session_description(music_session) + music_session.description end # grabs 1st genre - def session_genre(music_session_history) - genres_array = music_session_history.genres.nil? ? [] : music_session_history.genres.split(MusicSessionHistory::SEPARATOR) - genre = genres_array.length > 0 ? Genre.find_by_id(genres_array[0]) : nil - genre.nil? ? '' : genre.description + def session_genre(music_session) + music_session.genre.description end def recording_artist_name(recording) diff --git a/web/app/helpers/music_session_helper.rb b/web/app/helpers/music_session_helper.rb index a9e733f40..de1956c25 100644 --- a/web/app/helpers/music_session_helper.rb +++ b/web/app/helpers/music_session_helper.rb @@ -1,6 +1,6 @@ module MusicSessionHelper - def facebook_image_for_music_session_history(music_session) + def facebook_image_for_music_session(music_session) if music_session.band path = !music_session.band.large_photo_url.blank? ? music_session.band.large_photo_url : "/assets/web/logo-256.png" else @@ -10,8 +10,8 @@ module MusicSessionHelper request.protocol + request.host_with_port + path end - # careful; this mirrors logic of facebook_image_for_music_session_history - def facebook_image_size_for_music_session_history(music_session) + # careful; this mirrors logic of facebook_image_for_music_session + def facebook_image_size_for_music_session(music_session) if music_session.band !music_session.band.large_photo_url.blank? ? 200 : 256 else @@ -19,7 +19,7 @@ module MusicSessionHelper end end - def title_for_music_session_history(music_session, sharer = nil) + def title_for_music_session(music_session, sharer = nil) if music_session.band "LIVE SESSION: #{music_session.band.name}" else @@ -27,7 +27,7 @@ module MusicSessionHelper if sharer && unique_users.exists?(sharer) "LIVE SESSION: #{sharer.name}#{additional_member_count(unique_users)}" else - "LIVE SESSION: #{music_session.user.name}#{additional_member_count(unique_users)}" + "LIVE SESSION: #{music_session.creator.name}#{additional_member_count(unique_users)}" end end @@ -42,7 +42,7 @@ module MusicSessionHelper end end - def description_for_music_session_history(music_session) + def description_for_music_session(music_session) truncate(music_session.description, length:250) end end diff --git a/web/app/helpers/recording_helper.rb b/web/app/helpers/recording_helper.rb index d443c475d..0c7b4030d 100644 --- a/web/app/helpers/recording_helper.rb +++ b/web/app/helpers/recording_helper.rb @@ -10,7 +10,7 @@ module RecordingHelper request.protocol + request.host_with_port + path end - # careful; this mirrors logic of facebook_image_for_music_session_history + # careful; this mirrors logic of facebook_image_for_music_session def facebook_image_size_for_claimed_recording(claimed_recording) if claimed_recording.recording.band !claimed_recording.recording.band.large_photo_url.blank? ? 200 : 256 diff --git a/web/app/views/api_feeds/show.rabl b/web/app/views/api_feeds/show.rabl index eaa514c05..03a8ad2c5 100644 --- a/web/app/views/api_feeds/show.rabl +++ b/web/app/views/api_feeds/show.rabl @@ -1,10 +1,10 @@ object @feed -glue :music_session_history do +glue :music_session do node :type do |i| - 'music_session_history' + 'music_session' end attributes :id, :description, :genres, :created_at, :session_removed_at, :comment_count, :like_count, :play_count, :fan_access, :is_over?, :has_mount? @@ -12,7 +12,7 @@ glue :music_session_history do node do |history| { helpers: { - avatar: asset_path(resolve_avatarables(history.band, history.user)), + avatar: asset_path(resolve_avatarables(history.band, history.creator)), artist_name: session_artist_name(history), artist_id: session_artist_id(history), artist_datakey: session_artist_datakey(history), @@ -27,7 +27,7 @@ glue :music_session_history do } end - child(:user => :creator) { + child(:creator => :creator) { attributes :id, :first_name, :last_name, :photo_url } @@ -60,7 +60,7 @@ glue :music_session_history do attributes :id, :name, :location, :photo_url } - child(:music_session => :music_session) do + child(:active_music_session => :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_music_sessions/claimed_recording_start.rabl b/web/app/views/api_music_sessions/claimed_recording_start.rabl new file mode 100644 index 000000000..e34b6943d --- /dev/null +++ b/web/app/views/api_music_sessions/claimed_recording_start.rabl @@ -0,0 +1,3 @@ +object @music_session + +extends "api_music_sessions/show" \ No newline at end of file diff --git a/web/app/views/api_music_sessions/claimed_recording_stop.rabl b/web/app/views/api_music_sessions/claimed_recording_stop.rabl new file mode 100644 index 000000000..e34b6943d --- /dev/null +++ b/web/app/views/api_music_sessions/claimed_recording_stop.rabl @@ -0,0 +1,3 @@ +object @music_session + +extends "api_music_sessions/show" \ No newline at end of file diff --git a/web/app/views/api_music_sessions/create_legacy.rabl b/web/app/views/api_music_sessions/create_legacy.rabl new file mode 100644 index 000000000..e34b6943d --- /dev/null +++ b/web/app/views/api_music_sessions/create_legacy.rabl @@ -0,0 +1,3 @@ +object @music_session + +extends "api_music_sessions/show" \ No newline at end of file diff --git a/web/app/views/api_music_sessions/history_show.rabl b/web/app/views/api_music_sessions/history_show.rabl index 9d22fd4ab..40a5bbc52 100644 --- a/web/app/views/api_music_sessions/history_show.rabl +++ b/web/app/views/api_music_sessions/history_show.rabl @@ -1,33 +1,146 @@ object @history -attributes :id, :music_session_id, :description, :fan_access, :genres, :like_count, :comment_count, :created_at +if !current_user + # there should be more data returned, but we need to think very carefully about what data is public for a music session + attributes :id -node :share_url do |history| - unless history.share_token.nil? - share_token_url(history.share_token.token) + child(:active_music_session => :active_music_session) { + node do |music_session| + child({:mount => :mount}, :if => lambda { |music_session| music_session.fan_access}) { + attributes :id, :name, :sourced, :listeners, :bitrate, :subtype, :url + node(:mime_type) { |mount| mount.resolve_string(:mime_type) } + node(:bitrate) { |mount| mount.resolve_string(:bitrate) } + node(:subtype) { |mount| mount.resolve_string(:subtype) } + } + end + } +else + + attributes :id, :music_session_id, :description, :musician_access, :approval_required, :fan_access, :fan_chat, + :band_id, :user_id, :genre_id, :created_at, :like_count, :comment_count + + node :share_url do |history| + unless history.share_token.nil? + share_token_url(history.share_token.token) + end end -end -child(:user => :creator) { - attributes :name, :photo_url -} - -child(:band => :band) { - attributes :name, :photo_url -} - -child(:music_session_user_histories => :users) { - attributes :instruments - - child(:user => :user) { + child(:creator => :creator) { attributes :name, :photo_url } -} -child(:comments => :comments) { - attributes :comment, :created_at - - child(:user => :creator) { - attributes :id, :first_name, :last_name, :name, :photo_url, :musician + child(:band => :band) { + attributes :name, :photo_url } -} \ No newline at end of file + + child(:music_session_user_histories => :users) { + attributes :instruments + + child(:user => :user) { + attributes :name, :photo_url + } + } + + child(:comments => :comments) { + attributes :comment, :created_at + + child(:user => :creator) { + attributes :id, :first_name, :last_name, :name, :photo_url, :musician + } + } + + child(:active_music_session => :active_music_session) { + attributes :claimed_recording_initiator_id, :track_changes_counter + + node :genres do |item| + [item.genre.description] # XXX: need to return single genre; not array + end + + if :is_recording? + node do |music_session| + { :recording => partial("api_recordings/show", :object => music_session.recording) } + end + end + + node :share_url do |music_session| + unless music_session.music_session.share_token.nil? + share_token_url(music_session.music_session.share_token.token) + end + end + + child(:connections => :participants) { + collection @music_sessions, :object_root => false + attributes :ip_address, :client_id, :joined_session_at + + node :user do |connection| + { :id => connection.user.id, :photo_url => connection.user.photo_url, :name => connection.user.name, :is_friend => connection.user.friends?(current_user), :connection_state => connection.aasm_state } + end + + child(:tracks => :tracks) { + attributes :id, :connection_id, :instrument_id, :sound, :client_track_id, :updated_at + } + } + + + child({:invitations => :invitations}) { + attributes :id, :sender_id, :receiver_id + } + + # only show join_requests if the current_user is in the session + node(:join_requests, :if => lambda { |music_session| music_session.users.exists?(current_user) } ) do |music_session| + child(:join_requests => :join_requests) { + attributes :id, :text + child(:user => :user) { + attributes :id, :name + } + } + end + + # only show currently playing recording data if the current_user is in the session + node(:claimed_recording, :if => lambda { |music_session| music_session.users.exists?(current_user) } ) do |music_session| + + child(:claimed_recording => :claimed_recording) { + attributes :id, :name, :description, :is_public + + child(:recording => :recording) { + attributes :id, :created_at, :duration + child(:band => :band) { + attributes :id, :name + } + + child(:mixes => :mixes) { + attributes :id, :is_completed + + node :mp3_url do |mix| + mix[:mp3_url] + end + + node :ogg_url do |mix| + mix[:ogg_url] + end + } + + child(:recorded_tracks => :recorded_tracks) { + attributes :id, :fully_uploaded, :client_track_id, :client_id, :instrument_id + + node :url do |recorded_track| + recorded_track[:url] + end + + child(:user => :user) { + attributes :id, :first_name, :last_name, :city, :state, :country, :photo_url + } + } + } + } + end + + # 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 + node(:mime_type) { |mount| mount.resolve_string(:mime_type) } + node(:bitrate) { |mount| mount.resolve_string(:bitrate) } + node(:subtype) { |mount| mount.resolve_string(:subtype) } + } + } +end \ No newline at end of file diff --git a/web/app/views/api_music_sessions/show.rabl b/web/app/views/api_music_sessions/show.rabl index 0b1c30540..87fcfc683 100644 --- a/web/app/views/api_music_sessions/show.rabl +++ b/web/app/views/api_music_sessions/show.rabl @@ -16,7 +16,7 @@ else attributes :id, :description, :musician_access, :approval_required, :fan_access, :fan_chat, :band_id, :user_id, :claimed_recording_initiator_id, :track_changes_counter, :max_score node :genres do |item| - item.genres.map(&:description) + [item.genre.description] # XXX: need to return single genre; not array end if :is_recording? @@ -26,8 +26,8 @@ else end node :share_url do |music_session| - unless music_session.music_session_history.share_token.nil? - share_token_url(music_session.music_session_history.share_token.token) + unless music_session.music_session.share_token.nil? + share_token_url(music_session.music_session.share_token.token) end end diff --git a/web/app/views/clients/_rateSession.html.erb b/web/app/views/clients/_rateSession.html.erb new file mode 100644 index 000000000..fa87832f5 --- /dev/null +++ b/web/app/views/clients/_rateSession.html.erb @@ -0,0 +1,17 @@ +
+ +
+ <%= image_tag "shared/icon_session.png", {:height => 19, :width => 19, :class => "content-icon"} %> +

please rate your session

+
+
+
+       +

+ +

+ SEND FEEDBACK   NOT NOW, THANKS +
+
+ +
diff --git a/web/app/views/clients/gear/_gear_wizard.html.haml b/web/app/views/clients/gear/_gear_wizard.html.haml index fbeffefbd..57f0ef1f8 100644 --- a/web/app/views/clients/gear/_gear_wizard.html.haml +++ b/web/app/views/clients/gear/_gear_wizard.html.haml @@ -83,13 +83,18 @@ .wizard-step-column %h2 Test Results .ftue-box.results - .left.w50.gold-fill.center.white.scoring-section + .left.w50.center.white.scoring-section.latency-score-section .p5 .latency LATENCY %span.latency-score - .left.w50.green-fill.center.white.scoring-section + .left.w50.center.white.scoring-section.io-score-section .p5 .io I/O + %span.io-skip-msg + Skipped + %span.io-countdown + %span.secs + seconds left %span.io-rate-score %span.io-var-score diff --git a/web/app/views/clients/index.html.erb b/web/app/views/clients/index.html.erb index dd8d91baa..28836cda8 100644 --- a/web/app/views/clients/index.html.erb +++ b/web/app/views/clients/index.html.erb @@ -21,6 +21,7 @@ <%= render "clients/gear/gear_wizard" %> <%= render "terms" %> <%= render "leaveSessionWarning" %> +<%= render "rateSession" %> <%= render "alert" %> <%= render "sidebar" %> <%= render "createSession" %> @@ -304,7 +305,6 @@ window.jamClient = interceptedJamClient; - } // Let's get things rolling... diff --git a/web/app/views/layouts/ping.jnlp.erb b/web/app/views/layouts/ping.jnlp.erb index 3fc4fbeea..dc2c8adc1 100644 --- a/web/app/views/layouts/ping.jnlp.erb +++ b/web/app/views/layouts/ping.jnlp.erb @@ -1,20 +1,25 @@ - Ping - JamKazam + JamKazam Ping + JamKazam, Inc. + + - + - + - da1-cc=50.242.148.38:4442 + <%= yield(:hosts) %> -u<%= ApplicationHelper.base_uri(request) %>/api/users/isp_scoring -i<%= yield(:provider) %> -a + + + \ No newline at end of file diff --git a/web/app/views/music_sessions/show.html.erb b/web/app/views/music_sessions/show.html.erb index 9b3c3a56c..d55ab1f11 100644 --- a/web/app/views/music_sessions/show.html.erb +++ b/web/app/views/music_sessions/show.html.erb @@ -2,18 +2,18 @@ <% content_for :social_meta do %> - + - - - - + + + + - - + + <% end %>
@@ -30,13 +30,13 @@ <%= @music_session.band.name %> <% else %>
- <% unless @music_session.user.photo_url.blank? %> - <%= image_tag "#{@music_session.user.photo_url}", {:alt => ""} %> + <% unless @music_session.creator.photo_url.blank? %> + <%= image_tag "#{@music_session.creator.photo_url}", {:alt => ""} %> <% else %> <%= image_tag "shared/avatar_generic.png", {:alt => ""} %> <% end %>
- <%= @music_session.user.name %> + <%= @music_session.creator.name %> <% end %>
@@ -56,9 +56,9 @@ -
<%= @music_session.genres.split('|').first.capitalize unless @music_session.genres.blank? %>
+
<%= @music_session.genre.description %>
<%= @music_session.comment_count %> @@ -111,7 +111,7 @@ <% unless @music_session.band.nil? %> <%= render :partial => "shared/landing_sidebar", :locals => {:user => @music_session.band, :recent_history => @music_session.band.recent_history} %> <% else %> - <%= render :partial => "shared/landing_sidebar", :locals => {:user => @music_session.user, :recent_history => @music_session.user.recent_history} %> + <%= render :partial => "shared/landing_sidebar", :locals => {:user => @music_session.creator, :recent_history => @music_session.creator.recent_history} %> <% end %> <% else %> <%= render :partial => "shared/cta_sidebar" %> diff --git a/web/app/views/ping/ping.html.erb b/web/app/views/ping/ping.html.erb index 4f6bf6bb7..d2e8dcda2 100644 --- a/web/app/views/ping/ping.html.erb +++ b/web/app/views/ping/ping.html.erb @@ -1,25 +1,69 @@ Test Internet Latency + -

Test Internet Latency

-

Select the link corresponding to your internet service provider. - This will launch an applet to test the performance of your connection.

-

My ISP is AT&T

-

Click <%= link_to 'here', '/ping/pingat.jnlp' %>.

+

Internet Speed Test

+
-

My ISP is Comcast

-

Click <%= link_to 'here', '/ping/pingcc.jnlp' %>.

+

Welcome, and thank you for helping us by running this quick latency test application. The app may just run, or you might need to install or update the version of Java on your computer. It will take just one minute if you have Java already installed and up-to-date, or less than five minutes if you have to install or update Java on your computer.

-

My ISP is Time Warner

-

Click <%= link_to 'here', '/ping/pingtw.jnlp' %>.

+

Following are step-by-step directions to run this test:

+
    +
  1. Please run this test app from your home, not from a business or coffee shop or anywhere else - only from your home, as we need the data collected from home environments.
  2. -

    My ISP is Verizon

    -

    Click <%= link_to 'here', '/ping/pingvz.jnlp' %>.

    +
  3. Please run this test app on a Windows computer. It’s too hard to get it to run on a Mac, even though it’s theoretically possible.
  4. -

    My ISP is none of the above.

    -

    Click <%= link_to 'here', '/ping/pingno.jnlp' %>.

    +
  5. Please connect your Windows computer to your home router with an Ethernet cable rather than connecting wirelessly via WiFi. This is important to the accuracy of the data being collected, thank you!
  6. + +
  7. To start the test, please click the Run Test button on the right side of this page next to the ISP that provides Internet service to your home.
  8. + +
  9. When you click the Run Test button, a file will start to download in your browser. It may display a message like “This type of file can harm your computer. Do you want to keep it anyway?”. Please click the button that lets your browser go ahead and download and save the file. It’s just a little test app we wrote ourselves, so we know it’s safe, but we did not sign the app with a certificate, so you may get this warning message.
  10. + +
  11. When the file is downloaded, please click on the file to open and run it. If your version of Java is up-to-date, the app will run as described in step 8 below. If you get a message that Java is not up-to-date on your computer, please follow step 7 below to update Java on your computer.
  12. + +
  13. Click the prompt button to update Java. You are taken to the Java website. Click the red Free Java Download button. Then click the Agree & Start Free Download button. When the file is downloaded, click on the file to open/run it, and then follow the on-screen instructions to install the Java update. (Note: Watch out during the installation for the McAfee option, and uncheck that one to avoid getting McAfee installed on your computer.) After the Java update has installed, go back to the JamKazam test app webpage, and click on the Run Test button again next to the ISP that provides Internet service to your home.
  14. + +
  15. When you run the test app, a window will open and you’ll be prompted “Do you want this app to run?”. Please answer yes to let the app run. Then you’ll see a small window open, and you’ll see the test app running. This will take less than a minute. When it’s finished, it displays “Results posted, thank you for your time!”. You can close the window, and you are all done.
  16. +
+ +

Thanks again very much for helping us collect this Internet latency data!

+ +

Regards,
+ The JamKazam Team

+
+
+

Select the link corresponding to your internet service provider. + This will launch an applet to test the performance of your connection.

+ +

AT&T <%= link_to 'Run Test', '/ping/pingat.jnlp', :class=>'button' %>

+ +

Comcast <%= link_to 'Run Test', '/ping/pingcc.jnlp', :class=>'button' %>

+ +

Time Warner <%= link_to 'Run Test', '/ping/pingtw.jnlp', :class=>'button' %>

+ +

Verizon <%= link_to 'Run Test', '/ping/pingvz.jnlp', :class=>'button' %>

+ +

None Of The Above <%= link_to 'Run Test', '/ping/pingno.jnlp', :class=>'button' %>

+
- \ No newline at end of file + diff --git a/web/app/views/ping/pingat.jnlp.erb b/web/app/views/ping/pingat.jnlp.erb index da63cae0b..4516b7177 100755 --- a/web/app/views/ping/pingat.jnlp.erb +++ b/web/app/views/ping/pingat.jnlp.erb @@ -1 +1,8 @@ -<% provide(:provider, 'at') %> \ No newline at end of file +<% provide(:provider, 'at') %> + +<% content_for :hosts do %> + da1-vz=157.130.141.42:4442 + da1-tw=50.84.4.230:4442 + da1-cc=50.242.148.38:4442 + da2-at=12.251.184.250:4442 +<% end %> \ No newline at end of file diff --git a/web/app/views/ping/pingcc.jnlp.erb b/web/app/views/ping/pingcc.jnlp.erb index ccc2d8e7d..17a5dfe36 100755 --- a/web/app/views/ping/pingcc.jnlp.erb +++ b/web/app/views/ping/pingcc.jnlp.erb @@ -1 +1,8 @@ -<% provide(:provider, 'cc') %> \ No newline at end of file +<% provide(:provider, 'cc') %> + +<% content_for :hosts do %> + da1-vz=157.130.141.42:4442 + da1-tw=50.84.4.230:4442 + da1-cc=50.242.148.38:4442 + da2-at=12.251.184.250:4442 +<% end %> \ No newline at end of file diff --git a/web/app/views/ping/pingno.jnlp.erb b/web/app/views/ping/pingno.jnlp.erb index cd2a0b04e..bb065e4ff 100755 --- a/web/app/views/ping/pingno.jnlp.erb +++ b/web/app/views/ping/pingno.jnlp.erb @@ -1 +1,8 @@ -<% provide(:provider, 'no') %> \ No newline at end of file +<% provide(:provider, 'no') %> + +<% content_for :hosts do %> + da1-vz=157.130.141.42:4442 + da1-tw=50.84.4.230:4442 + da1-cc=50.242.148.38:4442 + da2-at=12.251.184.250:4442 +<% end %> \ No newline at end of file diff --git a/web/app/views/ping/pingtw.jnlp.erb b/web/app/views/ping/pingtw.jnlp.erb index e7d6a5b18..b5082b480 100755 --- a/web/app/views/ping/pingtw.jnlp.erb +++ b/web/app/views/ping/pingtw.jnlp.erb @@ -1 +1,8 @@ -<% provide(:provider, 'tw') %> \ No newline at end of file +<% provide(:provider, 'tw') %> + +<% content_for :hosts do %> + da1-vz=157.130.141.42:4442 + da1-tw=50.84.4.230:4442 + da1-cc=50.242.148.38:4442 + da2-at=12.251.184.250:4442 +<% end %> \ No newline at end of file diff --git a/web/app/views/ping/pingvz.jnlp.erb b/web/app/views/ping/pingvz.jnlp.erb index b31f5b3eb..25344ffee 100755 --- a/web/app/views/ping/pingvz.jnlp.erb +++ b/web/app/views/ping/pingvz.jnlp.erb @@ -1 +1,8 @@ -<% provide(:provider, 'vz') %> \ No newline at end of file +<% provide(:provider, 'vz') %> + +<% content_for :hosts do %> + da1-vz=157.130.141.42:4442 + da1-tw=50.84.4.230:4442 + da1-cc=50.242.148.38:4442 + da2-at=12.251.184.250:4442 +<% end %> \ No newline at end of file diff --git a/web/app/views/shared/_comments.html.erb b/web/app/views/shared/_comments.html.erb index 03f348550..a0057319f 100644 --- a/web/app/views/shared/_comments.html.erb +++ b/web/app/views/shared/_comments.html.erb @@ -17,11 +17,13 @@