diff --git a/db/manifest b/db/manifest index d4b3013d5..4b526266a 100755 --- a/db/manifest +++ b/db/manifest @@ -169,3 +169,4 @@ periodic_emails.sql remember_extra_scoring_data.sql indexing_for_regions.sql latency_tester.sql +scheduled_sessions_next_session_scheduled.sql \ No newline at end of file diff --git a/db/up/scheduled_sessions_next_session_scheduled.sql b/db/up/scheduled_sessions_next_session_scheduled.sql new file mode 100644 index 000000000..ac59d437a --- /dev/null +++ b/db/up/scheduled_sessions_next_session_scheduled.sql @@ -0,0 +1 @@ +alter table music_sessions add column next_session_scheduled BOOLEAN default null; \ No newline at end of file diff --git a/ruby/lib/jam_ruby/app/mailers/user_mailer.rb b/ruby/lib/jam_ruby/app/mailers/user_mailer.rb index 29ddc3e68..a292357f3 100644 --- a/ruby/lib/jam_ruby/app/mailers/user_mailer.rb +++ b/ruby/lib/jam_ruby/app/mailers/user_mailer.rb @@ -104,7 +104,6 @@ def new_musicians(user, new_musicians, host='www.jamkazam.com') @user, @new_musicians, @host = user, new_musicians, host - sendgrid_recipients([user.email]) sendgrid_substitute('@USERID', [user.id]) sendgrid_unique_args :type => "new_musicians" diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/new_musicians.html.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/new_musicians.html.erb index c7299aa3f..8551c23df 100644 --- a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/new_musicians.html.erb +++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/new_musicians.html.erb @@ -7,7 +7,7 @@ Hi <%= @user.first_name %>, <% link_style = "background-color:#ED3618; margin:0px 8px 0px 8px; border: solid 1px #F27861; outline: solid 2px #ED3618; padding:3px 10px; font-family:Raleway, Arial, Helvetica, sans-serif; font-size:12px; font-weight:300; cursor:pointer; color:#FC9; text-decoration:none;" %>

-<% @new_nearby.each do |user| %> +<% @new_musicians.each do |user| %>
<%= user.name %>
@@ -24,7 +24,7 @@ Hi <%= @user.first_name %>, <% end %>

-

There are currently <%= @new_nearby.size%> musicians on JamKazam with low enough latency Internet connections to you to support a good online session. To see ALL the JamKazam musicians with whom you may want to connect and play, view our Musicians page at: http://www.jamkazam.com/client#/musicians. +

There are currently <%= @new_musicians.size%> musicians on JamKazam with low enough latency Internet connections to you to support a good online session. To see ALL the JamKazam musicians with whom you may want to connect and play, view our Musicians page at: http://www.jamkazam.com/client#/musicians.

Best Regards,

diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/new_musicians.text.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/new_musicians.text.erb index f1b51b9d7..df39c005a 100644 --- a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/new_musicians.text.erb +++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/new_musicians.text.erb @@ -1,17 +1,17 @@ -New JamKazam Musicians in your Area +New Musicians You Should Check Out Hi <%= @user.first_name %>, The following new musicians have joined JamKazam within the last week, and have Internet connections with low enough latency to you that you can have a good online session together. We'd suggest that you look through the new musicians listed below to see if any match your musical interests, and if so, click through to their profile page on the JamKazam website to send them a message or a request to connect as a JamKazam friend: -<% @new_nearby.each do |user| %> +<% @new_musicians.each do |user| %> <%= user.name %> (http://<%= @host %>/client#/profile/<%= user.id %>) <%= user.location %> <% user.instruments.collect { |inst| inst.description }.join(', ') %> <%= user.biography %> <% end %> -There are currently <%= @new_nearby.size%> musicians on JamKazam with low enough latency Internet connections to you to support a good online session. To see ALL the JamKazam musicians with whom you may want to connect and play, view our Musicians page at: http://www.jamkazam.com/client#/musicians. +There are currently <%= @new_musicians.size%> musicians on JamKazam with low enough latency Internet connections to you to support a good online session. To see ALL the JamKazam musicians with whom you may want to connect and play, view our Musicians page at: http://www.jamkazam.com/client#/musicians. Best Regards, Team JamKazam diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/scheduled_session_daily.html.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/scheduled_session_daily.html.erb index 40788d8de..0d6ba9a6b 100644 --- a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/scheduled_session_daily.html.erb +++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/scheduled_session_daily.html.erb @@ -25,7 +25,7 @@ <%= sess.genre.description %> <%= sess.description %> -<%= sess.latency_store %> +<%= sess.latency %> <% end %> diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/scheduled_session_daily.text.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/scheduled_session_daily.text.erb index 16ce1e7c1..423c57f44 100644 --- a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/scheduled_session_daily.text.erb +++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/scheduled_session_daily.text.erb @@ -11,7 +11,7 @@ Take a look through these new sessions below, and just click the RSVP button on GENRE | DESCRIPTION | LATENCY <% @sessions_and_latency.each do |sess| %> -<%= sess.genre.description %> | <%= sess.description %> | <%= sess.latency_store %> +<%= sess.genre.description %> | <%= sess.description %> | <%= sess.latency %> <% end %> To see ALL the scheduled sessions that you might be interested in joining, view our Find Session page at: http://www.jamkazam.com/client#/findSession. diff --git a/ruby/lib/jam_ruby/models/email_batch_new_musician.rb b/ruby/lib/jam_ruby/models/email_batch_new_musician.rb index 819ecdbbc..9d33bf45e 100644 --- a/ruby/lib/jam_ruby/models/email_batch_new_musician.rb +++ b/ruby/lib/jam_ruby/models/email_batch_new_musician.rb @@ -9,7 +9,7 @@ module JamRuby VAR_MUSICIAN_TABLE = "@MUSICIAN_TABLE" TMP_NEW = 'tmp_new_musicians' - TMP_CAND = 'tmp_receiver_candidates' + TMP_RECEIVE = 'tmp_receivers' TMP_PAIRS = 'tmp_receivers_new_musicians' def self.subject @@ -21,77 +21,93 @@ module JamRuby ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS #{TMP_NEW}") sql =< '#{time_since_last_batch(SINCE_DAYS)}' SQL ActiveRecord::Base.connection.execute(sql) end - def _fetch_receiver_candidates - ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS #{TMP_CAND}") + # inserts eligible sessions to temp table + def _fetch_eligible_receivers + ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS #{TMP_RECEIVE}") sql =< '#{time_since_last_batch(SINCE_DAYS)}' AND + msess.created_at < '#{self.created_at}' AND scheduled_start >= '#{Time.now() + MIN_HOURS_START.hours}' AND (rrrs.rsvp_slot_id IS NULL OR rrrs.chosen != 't') SQL @@ -37,41 +39,64 @@ SQL end def _collect_eligible_recipients - ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS #{TMP_USER}") + ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS #{TMP_RECIP}") # load eligible recipients into tmp table sql =< offset + sql = "SELECT COUNT(DISTINCT receiver_id) AS num FROM #{TMP_MATCH}" + rr = ActiveRecord::Base.connection.execute(sql) + return 0 < rr.count ? rr[0]['num'].to_i : 0 + else + sql =< "music_session_id = '#{self.id}'") do |slot| + new_slot = RsvpSlot.new + new_slot.instrument_id = slot.instrument_id + new_slot.proficiency_level = slot.proficiency_level + new_session.rsvp_slots << new_slot + + # get the request for this slot that was approved (should only be ONE) + rsvp_request_slot = RsvpRequestRsvpSlot.where("chosen = true AND rsvp_slot_id = ?", slot.id).first + + unless rsvp_request_slot.nil? + rsvp = RsvpRequest.find_by_id(rsvp_request_slot.rsvp_request_id) + new_rsvp = RsvpRequest.new + new_rsvp.user_id = rsvp.user_id + + new_rsvp_req_slot = RsvpRequestRsvpSlot.new + new_rsvp_req_slot.rsvp_request = new_rsvp + new_rsvp_req_slot.rsvp_slot = new_slot + new_rsvp_req_slot.chosen = true + + # if this slot was not chosen, try to get any RSVPs that were 1-time cancellations and copy those + else + rejected_req_slots = RsvpRequestRsvpSlot.where("(chosen = false OR chosen is null) AND rsvp_slot_id = ?", slot.id).order("created_at ASC") + + rejected_req_slots.each do |req_slot| + # get RsvpRequest corresponding to this RsvpRequestRsvpSlot + rsvp = RsvpRequest.find_by_id(req_slot.rsvp_request_id) + + # if the RSVP was canceled (but all future sessions were NOT canceled), then copy this one and break + if rsvp.canceled && !rsvp.cancel_all + new_rsvp = RsvpRequest.new + new_rsvp.user_id = rsvp.user_id + + new_rsvp_req_slot = RsvpRequestRsvpSlot.new + new_rsvp_req_slot.rsvp_request = new_rsvp + new_rsvp_req_slot.rsvp_slot = new_slot + new_rsvp_req_slot.chosen = true + break + end + end + end + end + + # copy music_notations + MusicNotation.find_each(:conditions => "music_session_id = '#{self.id}'") do |notation| + new_notation = MusicNotation.new + new_notation.user_id = notation.user_id + new_notation.music_session = new_session + new_notation.file_url = notation.file_url + # new_notation.file_name = notation.file_name + new_notation.size = notation.size + new_session.music_notations << new_notation + end + + new_session.save + + # mark the next session as scheduled + self.next_session_scheduled = true + self.save + end + + end + def grouped_tracks tracks = [] self.music_session_user_histories.each do |msuh| diff --git a/ruby/lib/jam_ruby/models/rsvp_request.rb b/ruby/lib/jam_ruby/models/rsvp_request.rb index ed8ea3736..1eb651bd9 100644 --- a/ruby/lib/jam_ruby/models/rsvp_request.rb +++ b/ruby/lib/jam_ruby/models/rsvp_request.rb @@ -39,18 +39,18 @@ module JamRuby raise StateError, "Invalid session." end - # verify invitation exists for this user and session - invitation = Invitation.where("music_session_id = ? AND receiver_id = ?", music_session.id, user.id) - - if invitation.blank? && !music_session.open_rsvps - raise PermissionError, "Only a session invitee can create an RSVP for this session." - end - # verify slot IDs exist in request if params[:rsvp_slots].blank? raise StateError, "You must select at least 1 slot." end + # verify invitation exists for this user and session + invitation = Invitation.where("music_session_id = ? AND receiver_id = ?", music_session.id, user.id) + + if invitation.first.nil? && !music_session.open_rsvps + raise PermissionError, "Only a session invitee can create an RSVP for this session." + end + RsvpRequest.transaction do @rsvp = RsvpRequest.new @rsvp.user = user diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index ff061e29b..59b34c405 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -19,9 +19,6 @@ module JamRuby # 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, :mods_json - # used for temporary data store of latency between self and some other user - attr_accessor :latency_store - belongs_to :icecast_server_group, class_name: "JamRuby::IcecastServerGroup", inverse_of: :users, foreign_key: 'icecast_server_group_id' # authorizations (for facebook, etc -- omniauth) diff --git a/ruby/lib/jam_ruby/resque/scheduled/music_session_scheduler.rb b/ruby/lib/jam_ruby/resque/scheduled/music_session_scheduler.rb index e69de29bb..6babdd5ee 100644 --- a/ruby/lib/jam_ruby/resque/scheduled/music_session_scheduler.rb +++ b/ruby/lib/jam_ruby/resque/scheduled/music_session_scheduler.rb @@ -0,0 +1,39 @@ +require 'json' +require 'resque' +require 'resque-retry' +require 'net/http' +require 'digest/md5' + +module JamRuby + + class MusicSessionScheduler + extend Resque::Plugins::LonelyJob + + @queue = :music_session_scheduler + + @@log = Logging.logger[MusicSessionScheduler] + + def self.lock_timeout + 120 + end + + def self.perform + @@log.debug("waking up") + + JamWebEventMachine.run_wait_stop do + MusicSessionScheduler.new.run + end + + @@log.debug("done") + end + + def run + # get all weekly sessions that have ended in the last 15 minutes + criteria = "recurring_mode = 'weekly' AND session_removed_at is not null AND next_session_scheduled = false" + MusicSession.find_each(:conditions => criteria) do |music_session| + music_session.copy + end + end + + end +end \ No newline at end of file diff --git a/ruby/spec/factories.rb b/ruby/spec/factories.rb index 8d3c6cb61..99efdfc49 100644 --- a/ruby/spec/factories.rb +++ b/ruby/spec/factories.rb @@ -93,6 +93,7 @@ FactoryGirl.define do recurring_mode 'once' genre JamRuby::Genre.first association :creator, :factory => :user + open_rsvps false factory :recurring_music_session_weekly do recurring_mode 'weekly' @@ -476,21 +477,12 @@ FactoryGirl.define do end factory :rsvp_slot, class: JamRuby::RsvpSlot do - association :instrument, factory: :instrument - association :music_session, factory: :music_session - # association :rsvp_request_slot, factory: :rsvp_request_slot proficiency_level 'beginner' end factory :rsvp_request, class: JamRuby::RsvpRequest do - association :user, factory: :user - association :rsvp_slot, factory: :rsvp_slot - association :rsvp_request_slot, factory: :rsvp_request_slot canceled false - end - - factory :rsvp_request_slot, class: JamRuby::RsvpRequestRsvpSlot do - chosen false + cancel_all false end factory :latency_tester, :class => JamRuby::LatencyTester do diff --git a/ruby/spec/jam_ruby/models/email_batch_spec_new_musicians.rb b/ruby/spec/jam_ruby/models/email_batch_spec_new_musicians.rb index e259ed865..7d84c9594 100644 --- a/ruby/spec/jam_ruby/models/email_batch_spec_new_musicians.rb +++ b/ruby/spec/jam_ruby/models/email_batch_spec_new_musicians.rb @@ -15,49 +15,93 @@ describe EmailBatchNewMusician do let (:new_musician_batch) { FactoryGirl.create(:email_batch_new_musician) } - let (:receiver) { + let (:receiver_date) { Time.now - (EmailBatchNewMusician::SINCE_DAYS.days + 1.day) } + + let (:receiver1) { FactoryGirl.create(:user, - :created_at => Time.now - (EmailBatchNewMusician::SINCE_DAYS.days + 1.day), + :created_at => receiver_date, :last_jam_locidispid => 1, :last_jam_addr => 1 ) } + let (:receiver2) { + FactoryGirl.create(:user, + :created_at => receiver_date, + :last_jam_locidispid => 3, + :last_jam_addr => 3 + ) + } + + let (:drummer) { FactoryGirl.create(:user, :last_jam_locidispid => 1, :last_jam_addr => 1) } + let (:guitarist) { FactoryGirl.create(:user, :last_jam_locidispid => 1, :last_jam_addr => 1) } + let (:bassist) { FactoryGirl.create(:user, :last_jam_locidispid => 1, :last_jam_addr => 1) } + let (:vocalist) { FactoryGirl.create(:user, :last_jam_locidispid => 1, :last_jam_addr => 1) } + let (:loser) { FactoryGirl.create(:user, :last_jam_locidispid => 2, :last_jam_addr => 2) } + let (:drummer3) { FactoryGirl.create(:user, :last_jam_locidispid => 3, :last_jam_addr => 3) } + let (:guitarist3) { FactoryGirl.create(:user, :last_jam_locidispid => 3, :last_jam_addr => 3) } + let (:bassist3) { FactoryGirl.create(:user, :last_jam_locidispid => 3, :last_jam_addr => 3) } + let (:vocalist3) { FactoryGirl.create(:user, :last_jam_locidispid => 3, :last_jam_addr => 3) } - let (:drummer) { FactoryGirl.create(:user, - :last_jam_locidispid => 1, - :last_jam_addr => 1) } - let (:guitarist) { FactoryGirl.create(:user, - :last_jam_locidispid => 1, - :last_jam_addr => 1) } - let (:bassist) { FactoryGirl.create(:user, - :last_jam_locidispid => 1, - :last_jam_addr => 1) } - let (:vocalist) { FactoryGirl.create(:user, - :last_jam_locidispid => 1, - :last_jam_addr => 1) } - let (:loser) { FactoryGirl.create(:user, - :last_jam_locidispid => 2, - :last_jam_addr => 2) } before(:each) do - + drummer; guitarist; bassist; vocalist; loser + drummer3; guitarist3; bassist3; vocalist3 + receiver1; receiver2 + new_musician_batch.reset! + JamRuby::Score.delete_all JamRuby::Score.createx(1, 'a', 1, 1, 'a', 1, 10) JamRuby::Score.createx(1, 'a', 1, 2, 'a', 2, Score::MAX_YELLOW_LATENCY + 1) end it 'sets up data properly' do - receiver; drummer; loser; vocalist results = new_musician_batch.fetch_recipients expect(results.count).to eq(1) user, new_musicians = results[0] - expect(user.id).to eq(receiver.id) - expect(new_musicians.count).to eq(2) + expect(user.id).to eq(receiver1.id) + expect(new_musicians.count).to eq(4) end it 'sends email' do - pending - ebatch = new_musician_batch - ebatch.deliver_batch - expect(UserMailer.deliveries.length).to eq(3) + new_musician_batch.deliver_batch + expect(UserMailer.deliveries.length).to eq(1) + end + + it 'sends email periodically' do + EmailBatchNewMusician.send_new_musician_batch + expect(UserMailer.deliveries.length).to eq(1) + end + + it 'handles multiple receivers' do + JamRuby::Score.createx(1, 'a', 1, 3, 'a', 3, 10) + JamRuby::Score.createx(1, 'a', 1, 4, 'a', 4, 10) + JamRuby::Score.createx(2, 'a', 2, 4, 'a', 4, 10) + results = new_musician_batch.fetch_recipients + expect(results.count).to eq(2) + user, new_musicians = results.detect { |rr| rr[0].id == receiver1.id } + expect(new_musicians.count).to eq(8) + user, new_musicians = results.detect { |rr| rr[0].id == receiver2.id } + expect(new_musicians.count).to eq(4) + end + + it 'handles large batches' do + dd = receiver_date + 1.day + 20.downto(1) do |nn| + FactoryGirl.create(:user, :last_jam_locidispid => 5, :last_jam_addr => 5, :created_at => dd) + end + 10.downto(1) do |nn| + FactoryGirl.create(:user, :last_jam_locidispid => 6, :last_jam_addr => 6) + end + JamRuby::Score.delete_all + JamRuby::Score.createx(5, 'a', 5, 6, 'a', 6, 10) + JamRuby::Score.createx(5, 'a', 5, 7, 'a', 7, Score::MAX_YELLOW_LATENCY + 1) + FactoryGirl.create(:user, :last_jam_locidispid => 8, :last_jam_addr => 8, :created_at => dd) + FactoryGirl.create(:user, :last_jam_locidispid => 7, :last_jam_addr => 7) + + receivers = [] + new_musician_batch.fetch_recipients do |uu, newm| + receivers << uu + expect(newm.count).to eq(10) + end + expect(receivers.count).to eq(User.where(["created_at <= ? AND last_jam_addr = ?",dd,5]).count) end end diff --git a/ruby/spec/jam_ruby/models/email_batch_spec_scheduled_session.rb b/ruby/spec/jam_ruby/models/email_batch_spec_scheduled_session.rb index 316c65808..b632234bc 100644 --- a/ruby/spec/jam_ruby/models/email_batch_spec_scheduled_session.rb +++ b/ruby/spec/jam_ruby/models/email_batch_spec_scheduled_session.rb @@ -20,41 +20,34 @@ describe EmailBatchScheduledSessions do let (:bass) { FactoryGirl.create(:instrument, :description => 'bass') } let (:vocals) { FactoryGirl.create(:instrument, :description => 'vocal') } - let (:drummer) { FactoryGirl.create(:user, - :last_jam_locidispid => 1, - :last_jam_addr => 1) } - let (:guitarist) { FactoryGirl.create(:user, - :last_jam_locidispid => 1, - :last_jam_addr => 1) } - let (:bassist) { FactoryGirl.create(:user, - :last_jam_locidispid => 1, - :last_jam_addr => 1) } - let (:vocalist) { FactoryGirl.create(:user, - :last_jam_locidispid => 1, - :last_jam_addr => 1) } - let (:loser) { FactoryGirl.create(:user, - :last_jam_locidispid => 2, - :last_jam_addr => 2) } + let (:drummer) { FactoryGirl.create(:user, :last_jam_locidispid => 1, :last_jam_addr => 1) } + let (:guitarist) { FactoryGirl.create(:user, :last_jam_locidispid => 1, :last_jam_addr => 1) } + let (:bassist) { FactoryGirl.create(:user, :last_jam_locidispid => 1, :last_jam_addr => 1) } + let (:vocalist) { FactoryGirl.create(:user, :last_jam_locidispid => 1, :last_jam_addr => 1) } + let (:loser) { FactoryGirl.create(:user, :last_jam_locidispid => 2, :last_jam_addr => 2) } let (:session1) do FactoryGirl.create(:music_session, :creator => drummer, :scheduled_start => Time.now() + 2.days, :musician_access => true, - :approval_required => false) + :approval_required => false, + :created_at => Time.now - 1.hour) end let (:session2) do FactoryGirl.create(:music_session, :creator => drummer, :scheduled_start => Time.now() + 2.days, :musician_access => true, - :approval_required => false) + :approval_required => false, + :created_at => Time.now - 1.hour) end before(:each) do MusicianInstrument.delete_all RsvpSlot.delete_all JamRuby::Score.delete_all + scheduled_batch.reset! drummer.musician_instruments << FactoryGirl.build(:musician_instrument, user: drummer, instrument: drums, proficiency_level: 2) drummer.musician_instruments << FactoryGirl.build(:musician_instrument, user: drummer, instrument: guitar, proficiency_level: 2) @@ -73,7 +66,6 @@ describe EmailBatchScheduledSessions do FactoryGirl.create(:rsvp_slot, :instrument => drums, :music_session => session1) FactoryGirl.create(:rsvp_slot, :instrument => guitar, :music_session => session1) FactoryGirl.create(:rsvp_slot, :instrument => bass, :music_session => session1) - FactoryGirl.create(:rsvp_slot, :instrument => drums, :music_session => session2) FactoryGirl.create(:rsvp_slot, :instrument => guitar, :music_session => session2) FactoryGirl.create(:rsvp_slot, :instrument => bass, :music_session => session2) @@ -82,24 +74,54 @@ describe EmailBatchScheduledSessions do # oo = FactoryGirl.create(:rsvp_request, :user => vocalist, :rsvp_slot => oo) # oo.rsvp_request_slot.update_attributes(chosen: true) - JamRuby::Score.createx(1, 'a', 1, 1, 'a', 1, 10) - JamRuby::Score.createx(1, 'a', 1, 2, 'a', 2, Score::MAX_YELLOW_LATENCY + 1) - end - - before(:each) do end it 'sets up data properly' do + JamRuby::Score.createx(1, 'a', 1, 1, 'a', 1, 10) + JamRuby::Score.createx(1, 'a', 1, 2, 'a', 2, Score::MAX_YELLOW_LATENCY + 1) + expect(drummer.instruments.include?(drums)).to eq(true) expect(drummer.instruments.include?(guitar)).to eq(true) obj = scheduled_batch.fetch_recipients - expect(obj.count).to eq(3) + expect(obj.count).to eq(2) + + scheduled_batch.deliver_batch + expect(UserMailer.deliveries.length).to eq(2) end - it 'sends email' do - ebatch = scheduled_batch - ebatch.deliver_batch - expect(UserMailer.deliveries.length).to eq(3) + it 'handles large batches' do + creators = [] + 8.downto(1) do |nn| + creators << uu = FactoryGirl.create(:user, :last_jam_locidispid => 5, :last_jam_addr => 5) + msess = FactoryGirl.create(:music_session, + :creator => uu, + :scheduled_start => Time.now() + 2.days, + :musician_access => true, + :approval_required => false, + :created_at => Time.now - 1.hour) + FactoryGirl.create(:rsvp_slot, :instrument => drums, :music_session => msess) + FactoryGirl.create(:rsvp_slot, :instrument => guitar, :music_session => msess) + FactoryGirl.create(:rsvp_slot, :instrument => bass, :music_session => msess) + end + instruments = [drums, guitar, bass] + 4.downto(1) do |nn| + uu = FactoryGirl.create(:user, :last_jam_locidispid => 6, :last_jam_addr => 6) + uu.musician_instruments << FactoryGirl.build(:musician_instrument, + user: uu, + instrument: instruments.sample, + proficiency_level: 2) + end + JamRuby::Score.createx(5, 'a', 5, 6, 'a', 6, 10) + JamRuby::Score.createx(5, 'a', 5, 7, 'a', 7, Score::MAX_YELLOW_LATENCY + 1) + FactoryGirl.create(:user, :last_jam_locidispid => 8, :last_jam_addr => 8) + FactoryGirl.create(:user, :last_jam_locidispid => 7, :last_jam_addr => 7) + + receivers = [] + scheduled_batch.fetch_recipients(3) do |receiver, sessions| + receivers << receiver + expect(sessions.count).to eq(8) + end + expect(receivers.count).to eq(4) end end diff --git a/ruby/spec/jam_ruby/models/rsvp_request_spec.rb b/ruby/spec/jam_ruby/models/rsvp_request_spec.rb index 50a2ff2ef..551b53f0e 100644 --- a/ruby/spec/jam_ruby/models/rsvp_request_spec.rb +++ b/ruby/spec/jam_ruby/models/rsvp_request_spec.rb @@ -2,7 +2,162 @@ require 'spec_helper' describe RsvpRequest do - it "success" do - # FactoryGirl.create(:rsvp_request) + before(:each) do + RsvpRequestRsvpSlot.delete_all + RsvpRequest.delete_all + RsvpSlot.delete_all + Invitation.delete_all + MusicSession.delete_all + User.delete_all + + @session_invitee = FactoryGirl.create(:user) + @session_invitee.save + + @non_session_invitee = FactoryGirl.create(:user) + @non_session_invitee.save + + @session_creator = FactoryGirl.create(:user) + @session_creator.save + + # session invitations require sender and receiver to be friends + FactoryGirl.create(:friendship, :user => @session_invitee, :friend => @session_creator) + FactoryGirl.create(:friendship, :user => @session_creator, :friend => @session_invitee) + + @music_session = FactoryGirl.build(:music_session, :creator => @session_creator) + @music_session.save + + @slot1 = FactoryGirl.build(:rsvp_slot, :music_session => @music_session, :instrument => JamRuby::Instrument.find('electric guitar')) + @slot1.save + + @slot2 = FactoryGirl.build(:rsvp_slot, :music_session => @music_session, :instrument => JamRuby::Instrument.find('drums')) + @slot2.save + + @invitation = FactoryGirl.build(:invitation, :sender => @session_creator, :receiver => @session_invitee, :music_session => @music_session) + @invitation.save + end + + describe "create" do + it "should require a valid music session" do + expect {RsvpRequest.create({:session_id => "1234", :rsvp_slots => [@slot1.id, @slot2.id]}, @session_invitee)}.to raise_error(JamRuby::StateError) + end + + it "should require at least 1 slot" do + expect {RsvpRequest.create({:session_id => @music_session.id}, @session_invitee)}.to raise_error(JamRuby::StateError) + end + + it "should not allow user to RSVP for slot he has already RSVPed to" do + # allow open RSVPs + @music_session.open_rsvps = true + @music_session.save + + RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id]}, @non_session_invitee) + expect {RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id]}, @non_session_invitee)}.to raise_error(JamRuby::StateError) + end + + it "should allow invitee to RSVP to session with closed RSVPs" do + rsvp = RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id], :message => "Let's Jam!"}, @session_invitee) + + # verify comment + + # verify 2 notifications were created + end + + it "should allow non-invitee to RSVP to session with open RSVPs" do + # allow open RSVPs + @music_session.open_rsvps = true + @music_session.save + + expect {RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id]}, @non_session_invitee)}.to_not raise_error + + # verify notification was created + n = Notification.find_by_source_user_id(@non_session_invitee.id) + n.description.should == NotificationTypes::SCHEDULED_SESSION_RSVP + end + + it "should not allow user to RSVP to slot that has already been accepted" do + # allow open RSVPs + @music_session.open_rsvps = true + @music_session.save + + rsvp = RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id]}, @non_session_invitee) + + request_slots = RsvpRequestRsvpSlot.all + + # accept 1 of the slots + rs1 = RsvpRequestRsvpSlot.find_by_rsvp_slot_id(@slot1.id) + rs2 = RsvpRequestRsvpSlot.find_by_rsvp_slot_id(@slot2.id) + RsvpRequest.update({:id => rsvp.id, :session_id => @music_session.id, :rsvp_responses => [{:request_slot_id => rs1.id, :accept => true}, {:request_slot_id => rs2.id, :accept => false}]}, @session_creator) + + # attempt to create a request for the already accepted slot + expect {RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [rs1.id]}, @non_session_invitee)}.to raise_error(JamRuby::StateError) + end + + it "should not allow non-invitee to RSVP to session with closed RSVPs" do + expect {RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id]}, @non_session_invitee)}.to raise_error(JamRuby::PermissionError) + end + end + + describe "index" do + it "should allow retrieval of RSVPs by session" do + # allow open RSVPs + @music_session.open_rsvps = true + @music_session.save + + user2 = FactoryGirl.create(:user) + + RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id], :message => "Let's Jam!"}, @non_session_invitee) + RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id], :message => "Let's Jam!"}, user2) + + rsvps = RsvpRequest.index(@music_session) + rsvps.count.should == 2 + end + + it "should allow retrieval of RSVPs by session and user" do + # allow open RSVPs + @music_session.open_rsvps = true + @music_session.save + + user2 = FactoryGirl.create(:user) + + RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id, @slot2.id], :message => "Let's Jam!"}, @non_session_invitee) + RsvpRequest.create({:session_id => @music_session.id, :rsvp_slots => [@slot1.id], :message => "Let's Jam!"}, user2) + + rsvps = RsvpRequest.index(@music_session, @non_session_invitee) + rsvps.count.should == 1 + end + end + + describe "update" do + it "should only allow session organizer to approve request" do + # attempt to approve with non-organizer + + # approve with organizer + + # verify notification was created + end + + it "should not allow approval of RSVP for a slot that has already been approved" do + end + end + + + describe "cancel" do + it "should allow session organizer to cancel" do + end + + it "should allow RSVP creator to cancel" do + # test comment + + # verify notification was created + end + + it "should not allow anyone else to cancel" do + end + + it "should allow user to cancel a single session" do + end + + it "should allow user to cancel all future sessions" do + end end end \ No newline at end of file diff --git a/web/Gemfile b/web/Gemfile index e27ca71d1..5ac7dadc4 100644 --- a/web/Gemfile +++ b/web/Gemfile @@ -80,7 +80,7 @@ gem 'iso-639' gem 'language_list' group :development, :test do - gem 'rspec-rails' + gem 'rspec-rails', "2.99.0" gem "activerecord-import", "~> 0.4.1" gem 'guard-rspec', '0.5.5' gem 'jasmine', '1.3.1' diff --git a/web/app/assets/javascripts/findSession.js b/web/app/assets/javascripts/findSession.js index e9b2dd964..c6f40ffab 100644 --- a/web/app/assets/javascripts/findSession.js +++ b/web/app/assets/javascripts/findSession.js @@ -5,19 +5,17 @@ context.JK = context.JK || {}; context.JK.FindSessionScreen = function (app) { var CATEGORY = { - INVITATION: {index: 0, id: "table#sessions-invitations"}, - FRIEND: {index: 1, id: "table#sessions-friends"}, - OTHER: {index: 2, id: "table#sessions-other"} + ACTIVE: {index: 0, id: "table#sessions-active"}, + SCHEDULED: {index: 1, id: "table#sessions-scheduled"} }; var logger = context.JK.logger; var rest = context.JK.Rest(); var sessionLatency; var sessions = {}; - var invitationSessionGroup = {}; - var friendSessionGroup = {}; - var otherSessionGroup = {}; - var sessionCounts = [0, 0, 0]; + var activeSessionGroup = {}; + var scheduledSessionGroup = {}; + var sessionCounts = [0, 0]; var sessionList; var currentQuery = defaultQuery(); var currentPage = 0; @@ -105,47 +103,33 @@ function refreshDisplay() { var priorVisible; - var INVITATION = 'div#sessions-invitations'; - var FRIEND = 'div#sessions-friends'; - var OTHER = 'div#sessions-other'; + var ACTIVE = 'div#sessions-active'; + var SCHEDULED = 'div#sessions-scheduled'; - // INVITATION - //logger.debug("sessionCounts[CATEGORY.INVITATION.index]=" + sessionCounts[CATEGORY.INVITATION.index]); - if (sessionCounts[CATEGORY.INVITATION.index] === 0) { + // ACTIVE + //logger.debug("sessionCounts[CATEGORY.ACTIVE.index]=" + sessionCounts[CATEGORY.ACTIVE.index]); + if (sessionCounts[CATEGORY.ACTIVE.index] === 0) { priorVisible = false; - $(INVITATION).hide(); + $(ACTIVE).hide(); } else { priorVisible = true; - $(INVITATION).show(); + $(ACTIVE).show(); } // FRIEND if (!priorVisible) { - $(FRIEND).removeClass('mt35'); + $(SCHEDULED).removeClass('mt35'); } - //logger.debug("sessionCounts[CATEGORY.FRIEND.index]=" + sessionCounts[CATEGORY.FRIEND.index]); - if (sessionCounts[CATEGORY.FRIEND.index] === 0) { + //logger.debug("sessionCounts[CATEGORY.SCHEDULED.index]=" + sessionCounts[CATEGORY.SCHEDULED.index]); + if (sessionCounts[CATEGORY.SCHEDULED.index] === 0) { priorVisible = false; - $(FRIEND).hide(); + $(SCHEDULED).hide(); } else { priorVisible = true; - $(FRIEND).show(); - } - - // OTHER - if (!priorVisible) { - $(OTHER).removeClass('mt35'); - } - - //logger.debug("sessionCounts[CATEGORY.OTHER.index]=" + sessionCounts[CATEGORY.OTHER.index]); - if (sessionCounts[CATEGORY.OTHER.index] === 0) { - $(OTHER).hide(); - } - else { - $(OTHER).show(); + $(SCHEDULED).show(); } } @@ -272,9 +256,9 @@ * of the renderSession method without having to do * as much heavy setup. */ - function setSession(session) { - invitationSessionGroup[session.id] = session; - } + // function setSession(session) { + // invitationSessionGroup[session.id] = session; + // } /** * Render a single session line into the table. @@ -285,16 +269,12 @@ // store session in the appropriate bucket and increment category counts var session = sessions[sessionId]; if (containsInvitation(session)) { - invitationSessionGroup[sessionId] = session; - sessionCounts[CATEGORY.INVITATION.index]++; + activeSessionGroup[sessionId] = session; + sessionCounts[CATEGORY.ACTIVE.index]++; } else if (containsFriend(session)) { - friendSessionGroup[sessionId] = session; - sessionCounts[CATEGORY.FRIEND.index]++; - } - else { - otherSessionGroup[sessionId] = session; - sessionCounts[CATEGORY.OTHER.index]++; + scheduledSessionGroup[sessionId] = session; + sessionCounts[CATEGORY.SCHEDULED.index]++; } // hack to prevent duplicate rows from being rendered when filtering @@ -303,25 +283,18 @@ logger.debug('Rendering session ID = ' + sessionId); - if (invitationSessionGroup[sessionId] != null) { - $tbGroup = $(CATEGORY.INVITATION.id); + if (activeSessionGroup[sessionId] != null) { + $tbGroup = $(CATEGORY.ACTIVE.id); - if ($("table#sessions-invitations tr[id='" + sessionId + "']").length > 0) { + if ($("table#sessions-active tr[id='" + sessionId + "']").length > 0) { sessionAlreadyRendered = true; } } - else if (friendSessionGroup[sessionId] != null) { + else if (scheduledSessionGroup[sessionId] != null) { ; - $tbGroup = $(CATEGORY.FRIEND.id); + $tbGroup = $(CATEGORY.SCHEDULED.id); - if ($("table#sessions-friends tr[id='" + sessionId + "']").length > 0) { - sessionAlreadyRendered = true; - } - } - else if (otherSessionGroup[sessionId] != null) { - $tbGroup = $(CATEGORY.OTHER.id); - - if ($("table#sessions-other tr[id='" + sessionId + "']").length > 0) { + if ($("table#sessions-scheduled tr[id='" + sessionId + "']").length > 0) { sessionAlreadyRendered = true; } } @@ -359,16 +332,14 @@ currentPage = 0; $noMoreSessions.hide(); $scroller.infinitescroll('resume'); - $('table#sessions-invitations').children(':not(:first-child)').remove(); - $('table#sessions-friends').children(':not(:first-child)').remove(); - $('table#sessions-other').children(':not(:first-child)').remove(); + $('table#sessions-active').children(':not(:first-child)').remove(); + $('table#sessions-scheduled').children(':not(:first-child)').remove(); - sessionCounts = [0, 0, 0]; + sessionCounts = [0, 0]; sessions = {}; - invitationSessionGroup = {}; - friendSessionGroup = {}; - otherSessionGroup = {}; + activeSessionGroup = {}; + scheduledSessionGroup = {}; } function deleteSession(evt) { @@ -480,7 +451,7 @@ this.afterShow = afterShow; // Following exposed for easier testing. - this.setSession = setSession; + // this.setSession = setSession; this.clearResults = clearResults; this.getCategoryEnum = getCategoryEnum; diff --git a/web/app/assets/javascripts/hoverSession.js b/web/app/assets/javascripts/hoverSession.js index 5bedf9249..19cc5b3da 100644 --- a/web/app/assets/javascripts/hoverSession.js +++ b/web/app/assets/javascripts/hoverSession.js @@ -50,14 +50,28 @@ var template = $('#template-hover-session').html(); + var sessionPageUrl = "/sessions/" + response.id; + var sessionPageLinkText = "WEB PAGE"; + + // if session hasn't begun, it must be a scheduled session so link to SESSION INFO page + if (!response.active_music_session && !response.session_removed_at) { + sessionPageUrl += "/details"; + sessionPageLinkText = "SESSION DETAILS"; + } + var sessionHtml = context.JK.fillTemplate(template, { musicSessionId: response.id, + name: response.name, description: response.description, - genre: response.genres.toUpperCase(), + genre: response.genre_id.toUpperCase(), comment_count: response.comment_count, like_count: response.like_count, created_at: $.timeago(response.created_at), - musicians: musicianHtml + musicians: musicianHtml, + url: sessionPageUrl, + externalLinkText: sessionPageLinkText, + start_time: response.scheduled_start, + recurrence: response.recurring_mode === 'weekly' ? 'Recurs weekly on this day at this time' : '' }); $(hoverSelector).append('

Session Detail

' + sessionHtml); diff --git a/web/app/assets/javascripts/inviteMusicians.js b/web/app/assets/javascripts/inviteMusicians.js index 042260865..604641e1d 100644 --- a/web/app/assets/javascripts/inviteMusicians.js +++ b/web/app/assets/javascripts/inviteMusicians.js @@ -138,11 +138,17 @@ } function getInvitedFriends() { - return invitedFriendNames; + return invitedFriends; } this.getInvitedFriends = getInvitedFriends; + function getInvitedFriendNames() { + return invitedFriendNames; + } + + this.getInvitedFriendNames = getInvitedFriendNames; + function removeInvitation(evt) { var idx = invitedFriends.indexOf($(evt.currentTarget).parent().attr('user-id')); if (0 <= idx) { diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js index d8caf2adc..e5792b3ff 100644 --- a/web/app/assets/javascripts/jam_rest.js +++ b/web/app/assets/javascripts/jam_rest.js @@ -56,6 +56,19 @@ }); } + function uploadMusicNotations(formData) { + return $.ajax({ + type: "POST", + processData: false, + contentType: false, + dataType: "json", + cache: false, + async: false, + url: "/api/music_notations", + data: formData + }); + } + function legacyJoinSession(options) { var sessionId = options["session_id"]; delete options["session_id"]; @@ -1114,6 +1127,7 @@ this.initialize = initialize; this.legacyCreateSession = legacyCreateSession; this.createScheduledSession = createScheduledSession; + this.uploadMusicNotations = uploadMusicNotations; this.legacyJoinSession = legacyJoinSession; this.joinSession = joinSession; this.getUserDetail = getUserDetail; diff --git a/web/app/assets/javascripts/notificationPanel.js b/web/app/assets/javascripts/notificationPanel.js index d136a583d..d7731f0c9 100644 --- a/web/app/assets/javascripts/notificationPanel.js +++ b/web/app/assets/javascripts/notificationPanel.js @@ -214,6 +214,7 @@ var notificationHtml = context.JK.fillTemplate(template, { notificationId: val.notification_id, sessionId: val.session_id, + hoveraction: val.session_id ? "session" : "", avatar_url: context.JK.resolveAvatarUrl(val.photo_url), text: val.formatted_msg, date: $.timeago(val.created_at) @@ -358,6 +359,43 @@ moreTextLink.hide(); } } + else if (type === context.JK.MessageType.SCHEDULED_SESSION_INVITATION) { + linkSessionInfoNotification(payload, $notification, $btnNotificationAction); + } + else if (type === context.JK.MessageType.SCHEDULED_SESSION_RSVP) { + linkSessionInfoNotification(payload, $notification, $btnNotificationAction); + } + else if (type === context.JK.MessageType.SCHEDULED_SESSION_RSVP_APPROVED) { + linkSessionInfoNotification(payload, $notification, $btnNotificationAction); + } + else if (type === context.JK.MessageType.SCHEDULED_SESSION_RSVP_CANCELLED) { + linkSessionInfoNotification(payload, $notification, $btnNotificationAction); + } + else if (type === context.JK.MessageType.SCHEDULED_SESSION_RSVP_CANCELLED_ORG) { + linkSessionInfoNotification(payload, $notification, $btnNotificationAction); + } + else if (type === context.JK.MessageType.SCHEDULED_SESSION_CANCELLED) { + linkSessionInfoNotification(payload, $notification, $btnNotificationAction); + } + else if (type === context.JK.MessageType.SCHEDULED_SESSION_RESCHEDULED) { + linkSessionInfoNotification(payload, $notification, $btnNotificationAction); + } + else if (type === context.JK.MessageType.SCHEDULED_SESSION_REMINDER) { + linkSessionInfoNotification(payload, $notification, $btnNotificationAction); + } + else if (type === context.JK.MessageType.SCHEDULED_SESSION_COMMENT) { + linkSessionInfoNotification(payload, $notification, $btnNotificationAction); + } + + context.JK.bindHoverEvents($("#sidebar-div")); + } + + function linkSessionInfoNotification(payload, $notification, $btnNotificationAction) { + var $action_btn = $notification.find($btnNotificationAction); + $action_btn.text('SESSION DETAILS'); + $action_btn.click(function() { + context.JK.popExternalLink('/sessions/' + payload.session_id + '/details'); + }); } function acceptBandInvitation(args) { @@ -1110,6 +1148,7 @@ var notificationHtml = context.JK.fillTemplate(template, { notificationId: payload.notification_id, sessionId: payload.session_id, + hoveraction: payload.session_id ? "session" : "", avatar_url: context.JK.resolveAvatarUrl(payload.photo_url), text: payload.msg instanceof jQuery ? payload.msg.html() : payload.msg , date: $.timeago(payload.created_at) diff --git a/web/app/assets/javascripts/scheduled_session.js b/web/app/assets/javascripts/scheduled_session.js index 4ba3858b7..2c73a2f6e 100644 --- a/web/app/assets/javascripts/scheduled_session.js +++ b/web/app/assets/javascripts/scheduled_session.js @@ -41,12 +41,6 @@ // Step4 layout var $policyTypes = null; - var sessionStep1 = null; - var sessionStep2 = null; - var sessionStep3 = null; - var sessionStep4 = null; - var sessionStep5 = null; - var TOTAL_STEPS = 5; var STEP_SELECT_TYPE = 0; var STEP_SELECT_PLAYING = 1; @@ -171,7 +165,7 @@ var sessionNotations = []; for (var i = 0; i < createSessionSettings.notations.length; i++) { - var name = createSessionSettings.notations.item(i).name; + var name = createSessionSettings.notations.filename; sessionNotations.push(name); } $('#session-notations-disp').html(sessionNotations.join(', ')); @@ -179,7 +173,7 @@ $('#session-language-disp').html(createSessionSettings.language.label); var sessionInvited = []; - var invitedFriends = inviteMusiciansUtil.getInvitedFriends(); + var invitedFriends = inviteMusiciansUtil.getInvitedFriendNames(); $.each(invitedFriends, function(index, friend) { sessionInvited.push(friend); }); @@ -219,9 +213,9 @@ function beforeMoveStep1() { if (createSessionSettings.createType == 'start-scheduled') { var session = scheduledSessions[createSessionSettings.selectedSessionId]; - createSessionSettings.startDate = new Date(session.scheduled_start).toDateString(); + createSessionSettings.startDate = new Date(session.scheduled_start_time).toDateString(); createSessionSettings.startTime = getFormattedTime(new Date(session.scheduled_start), false); - createSessionSettings.genresValues = [session.genre.description]; + createSessionSettings.genresValues = session.genres; createSessionSettings.genres = [session.genre_id]; createSessionSettings.timezone.label = session.timezone_description; createSessionSettings.timezone.value = session.timezone; @@ -233,6 +227,7 @@ createSessionSettings.session_policy = session.legal_policy; createSessionSettings.musician_access.label = session.musician_access_description; createSessionSettings.fans_access.label = session.fan_access_description; + createSessionSettings.recurring_mode.value = session.recurring_mode; } else if (createSessionSettings.createType == 'quick-start') { createSessionSettings.genresValues = ['Pop']; @@ -249,6 +244,8 @@ createSessionSettings.musician_access.value = "only-rsvp"; createSessionSettings.fans_access.label = "Fans may not listen to session"; createSessionSettings.fans_access.value = "no-listen-chat"; + createSessionSettings.recurring_mode.label = 'Not Recurring'; + createSessionSettings.recurring_mode.value = 'once'; } else { createSessionSettings.startDate = $('#session-start-date').val(); @@ -303,18 +300,46 @@ $('#divSessionGenre').removeClass("error"); } - createSessionSettings.genres = genres; - createSessionSettings.genresValues = genresValues; - createSessionSettings.name = name; - createSessionSettings.description = description; - createSessionSettings.notations = $('#session-step-2 #session-select-files').get(0).files; + if (isValid) { + createSessionSettings.genres = genres; + createSessionSettings.genresValues = genresValues; + createSessionSettings.name = name; + createSessionSettings.description = description; + createSessionSettings.notations = $('#session-step-2 #session-select-files').get(0).files; + + if (createSessionSettings.notations.length > 0) { + var formData = new FormData(); + $.each(createSessionSettings.notations, function(i, file) { + formData.append('files[]', file); + }); + formData.append('client_id', app.clientId); + + rest.uploadMusicNotations(formData) + .done(function(response) { + var error_files = []; + $.each(response, function(i, music_notation) { + if (music_notation.errors) { + error_files.push(createSessionSettings.notations[i].name); + } + }) + if (error_files.length > 0) { + app.notifyAlert("Failed to upload files. ", error_files.join(', ')); + } + + createSessionSettings.notations = response; + }) + .fail(function(jqXHR) { + app.notifyServerError(jqXHR, "Unable to upload music notations"); + }) + } + } return isValid; } function beforeMoveStep3() { var $languageList = $('#session-language-list'); - createSessionSettings.language.label = $languageList.val(); + createSessionSettings.language.value = $languageList.val(); createSessionSettings.language.label = $languageList.get(0).options[$languageList.get(0).selectedIndex].text; return true; } @@ -377,11 +402,14 @@ data.legal_policy = createSessionSettings.session_policy; data.legal_terms = true; data.language = createSessionSettings.language.value; - if (createSessionSettings.createType == 'quick-start') { + if (createSessionSettings.createType == 'quick-start' || createSessionSettings.createType == 'immediately') { data.start = new Date().toDateString() + ' ' + getFormattedTime(new Date(), false); } + else { + data.start = createSessionSettings.startDate + ' ' + createSessionSettings.startTime; + } data.invitations = inviteMusiciansUtil.getInvitedFriends(); - data.recurring_mode = createSessionSettings.recurring_mode; + data.recurring_mode = createSessionSettings.recurring_mode.value; data.music_notations = createSessionSettings.music_notations; if (createSessionSettings.createType == 'start-scheduled') { @@ -398,12 +426,33 @@ } var joinSession = function(sessionId) { + var options = {}; + options.client_id = app.clientId; + options.session_id = sessionId; + options.as_musician = true; + options.tracks = tracks; + rest.joinSession(options) + .done(function(response) { var invitationCount = data.invitations.length; context.location = '/client#/session/' + sessionId; 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"); + } + }) }; if (createSessionSettings.createType == 'start-scheduled') { @@ -415,12 +464,16 @@ var newSessionId = response.id; $(".btn-next").off('click'); - if (createSessionSettings.createType == 'quick-start') { + if (createSessionSettings.createType == 'quick-start' || createSessionSettings.createType == "immediately") { joinSession(newSessionId); } + else { + app.notifyAlert("Session is successfully published."); + context.location = '/client#/home'; + } }) .fail(function(jqXHR){ - app.notifyServerError(jqXHR, "Unable to Create Session"); + app.notifyServerError(jqXHR, "Unable to schedule a session"); }); } } @@ -525,12 +578,12 @@ $btnBack.hide(); } - if (step == STEP_SELECT_TYPE && createSessionSettings.createType == 'start-scheduled' && createSessionSettings.selectedSessionId == null) { - $btnNext.removeClass('button-orange').addClass('button-grey'); - } - else { - $btnNext.removeClass('button-grey').addClass('button-orange'); - } +// if (step == STEP_SELECT_TYPE && createSessionSettings.createType == 'start-scheduled' && createSessionSettings.selectedSessionId == null) { +// $btnNext.removeClass('button-orange').addClass('button-grey'); +// } +// else { +// $btnNext.removeClass('button-grey').addClass('button-orange'); +// } if (step == STEP_SELECT_CONFIRM) { $btnNext.html(createSessionSettings.startType); @@ -697,6 +750,9 @@ inviteMusiciansUtil.loadFriends(); context.JK.guardAgainstBrowser(app); + + context.JK.dropdown($('#session-musician-access')); + context.JK.dropdown($('#session-fans-access')); } function changeSelectedFiles() { @@ -786,6 +842,14 @@ if (createSessionSettings.createType == 'start-scheduled') { $('#start-scheduled-wrapper').show(); $('#schedule-future-wrapper').hide(); + createSessionSettings = { + createType: 'start-scheduled', + timezone: {}, + recurring_mode: {}, + language: {}, + musician_access: {}, + fans_access: {} + }; } else if (createSessionSettings.createType == 'schedule-future') { $('#start-scheduled-wrapper').hide(); diff --git a/web/app/assets/javascripts/web/scheduled_session.js b/web/app/assets/javascripts/web/session_info.js similarity index 100% rename from web/app/assets/javascripts/web/scheduled_session.js rename to web/app/assets/javascripts/web/session_info.js diff --git a/web/app/assets/javascripts/web/web.js b/web/app/assets/javascripts/web/web.js index edf1ca888..819bbb975 100644 --- a/web/app/assets/javascripts/web/web.js +++ b/web/app/assets/javascripts/web/web.js @@ -53,7 +53,7 @@ //= require web/downloads //= require web/congratulations //= require web/sessions -//= require web/scheduled_session +//= require web/session_info //= require web/recordings //= require web/welcome //= require banner diff --git a/web/app/controllers/api_music_notations_controller.rb b/web/app/controllers/api_music_notations_controller.rb index a3c150cba..b90085c64 100644 --- a/web/app/controllers/api_music_notations_controller.rb +++ b/web/app/controllers/api_music_notations_controller.rb @@ -12,18 +12,18 @@ class ApiMusicNotationsController < ApiController raise JamArgumentError, "client_id must be specified" end - @music_notation = MusicNotation.new - @music_notation.client_id = client_id - @music_notation.file_url = params[:file] - @music_notation.user = current_user - @music_notation.save + @music_notations = [] - if @music_notation.errors.any? - response.status = :unprocessable_entity - respond_with @music_notation - else - respond_with @music_notation, responder: ApiResponder, :statue => 201 - end + params[:files].each do |file| + music_notation = MusicNotation.new + music_notation.file_url = file + music_notation.user = current_user + music_notation.save + + @music_notations.push music_notation + end if params[:files] + + respond_with @music_notations, responder: ApiResponder, :statue => 201 end def download diff --git a/web/app/views/api_music_notations/create.rabl b/web/app/views/api_music_notations/create.rabl index c946120c4..3d2f185b2 100644 --- a/web/app/views/api_music_notations/create.rabl +++ b/web/app/views/api_music_notations/create.rabl @@ -1,3 +1,3 @@ object @music_notations -attribute :id \ No newline at end of file +attribute :id, :filename \ No newline at end of file diff --git a/web/app/views/api_music_sessions/show_history.rabl b/web/app/views/api_music_sessions/show_history.rabl index 544586d7b..444e06a3c 100644 --- a/web/app/views/api_music_sessions/show_history.rabl +++ b/web/app/views/api_music_sessions/show_history.rabl @@ -19,7 +19,7 @@ else attributes :id, :music_session_id, :name, :description, :musician_access, :approval_required, :fan_access, :fan_chat, :band_id, :user_id, :genre_id, :created_at, :like_count, :comment_count, :scheduled_start, :scheduled_duration, :language, :recurring_mode, :language_description, :scheduled_start_time, :access_description, :timezone, :timezone_description, - :musician_access_description, :fan_access_description + :musician_access_description, :fan_access_description, :session_removed_at, :legal_policy node :share_url do |history| unless history.share_token.nil? @@ -27,6 +27,10 @@ else end end + node :genres do |item| + [item.genre.description] # XXX: need to return single genre; not array + end + child(:creator => :creator) { attributes :name, :photo_url } @@ -51,10 +55,6 @@ else } } - child(:genre => :genre) { - attributes :description - } - child(:session_info_comments => :session_info_comments) { attributes :comment, :created_at @@ -66,10 +66,15 @@ else child(:music_notations => :music_notations) { node do |music_notation| attributes :id - node(:filename) { |music_notation| music_notation.filename } + # note(:filename) { |music_notation| music_notation.filename } end } + child({:invitations => :invitations}) { + attributes :id, :sender_id, :receiver_id + } + + child(:active_music_session => :active_music_session) { attributes :claimed_recording_initiator_id, :track_changes_counter @@ -103,9 +108,6 @@ else } - 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| diff --git a/web/app/views/clients/_findSession.html.erb b/web/app/views/clients/_findSession.html.erb index 42cabf7d6..467852aaa 100644 --- a/web/app/views/clients/_findSession.html.erb +++ b/web/app/views/clients/_findSession.html.erb @@ -35,14 +35,11 @@
-
- <%= render :partial => "sessionList", :locals => {:title => "sessions you're invited to", :category => "sessions-invitations"} %> +
+ <%= render :partial => "sessionList", :locals => {:title => "current, active sessions", :category => "sessions-active"} %>
- <%= render :partial => "sessionList", :locals => {:title => "sessions with friends or bandmates", :category => "sessions-friends"} %> -
-
- <%= render :partial => "sessionList", :locals => {:title => "other sessions", :category => "sessions-other"} %> + <%= render :partial => "sessionList", :locals => {:title => "future, scheduled sessions", :category => "sessions-scheduled"} %>
Next
diff --git a/web/app/views/clients/_hoverSession.html.erb b/web/app/views/clients/_hoverSession.html.erb index 24af6b69a..a25d0493e 100644 --- a/web/app/views/clients/_hoverSession.html.erb +++ b/web/app/views/clients/_hoverSession.html.erb @@ -20,6 +20,7 @@
{genre}
{created_at}

+
{name}
{description}
{comment_count}     @@ -30,12 +31,16 @@ {musicians}
-

+
+ SCHEDULE:
+
Starts at {start_time}
+
{recurrence}
+


diff --git a/web/app/views/clients/_scheduledSession.html.erb b/web/app/views/clients/_scheduledSession.html.erb index 0e5e52579..3a8a6d4b8 100644 --- a/web/app/views/clients/_scheduledSession.html.erb +++ b/web/app/views/clients/_scheduledSession.html.erb @@ -27,35 +27,35 @@
    -
  • +
  • -
  • +
  • -
  • +
  • -
  • +
  • -
  • +