* VRFS-734 - adding all user progression tracking that is possible currently

This commit is contained in:
Seth Call 2013-09-30 02:37:22 +00:00
parent 6518e3a009
commit f38ca2721a
24 changed files with 622 additions and 14 deletions

View File

@ -72,3 +72,4 @@ isp_score_batch.sql
crash_dumps.sql
crash_dumps_idx.sql
music_sessions_user_history_add_session_removed_at.sql
user_progress_tracking.sql

View File

@ -0,0 +1,15 @@
-- tracks how users are progessing through the site: https://jamkazam.atlassian.net/wiki/pages/viewpage.action?pageId=3375145
alter table music_sessions_user_history add column max_concurrent_connections integer;
alter table music_sessions_user_history add column rating integer;
alter table users add column last_failed_certified_gear_at TIMESTAMP;
alter table users add column last_failed_certified_gear_reason varchar(256);
alter table users add column first_downloaded_client_at TIMESTAMP;
alter table users add column first_ran_client_at TIMESTAMP;
alter table users add column first_certified_gear_at TIMESTAMP;
alter table users add column first_music_session_at TIMESTAMP;
alter table users add column first_real_music_session_at TIMESTAMP;
alter table users add column first_good_music_session_at TIMESTAMP;
alter table users add column first_invited_at TIMESTAMP;
alter table users add column first_friended_at TIMESTAMP;
alter table users add column first_social_promoted_at TIMESTAMP;

View File

@ -263,8 +263,7 @@ SQL
if result.cmd_tuples == 1
# music session deleted!
@log.debug("deleted music session #{previous_music_session_id}")
JamRuby::MusicSessionHistory.removed_music_session(user_id,
previous_music_session_id)
JamRuby::MusicSessionHistory.removed_music_session(previous_music_session_id)
elsif 1 < result.cmd_tuples
msg = "music_sessions table data integrity violation; multiple rows found with music_session_id=#{previous_music_session_id}"
@log.error(msg)
@ -296,6 +295,7 @@ SQL
raise ActiveRecord::Rollback
else
blk.call(db_conn, connection) unless blk.nil?
user.update_progression_field(:first_music_session_at)
MusicSessionUserHistory.save(music_session_id, user_id, client_id)
end
end

View File

@ -8,6 +8,8 @@ module JamRuby
belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "user_id", :inverse_of => :inverse_friendships
belongs_to :friend, :class_name => "JamRuby::User", :foreign_key => "friend_id", :inverse_of => :friendships
after_save :track_user_progression
def self.save(user_id, friend_id)
friendship = Friendship.where("user_id='#{user_id}' AND friend_id='#{friend_id}'")
@ -60,5 +62,9 @@ module JamRuby
friends = friends.limit(options[:limit])
return friends
end
def track_user_progression
self.user.update_progression_field(:first_friended_at)
end
end
end

View File

@ -22,12 +22,18 @@ module JamRuby
validate :not_accepted_twice
validate :can_invite?
after_save :track_user_progression
# ensure invitation code is always created
before_validation(:on => :create) do
self.invitation_code = SecureRandom.urlsafe_base64 if self.invitation_code.nil?
self.sender_id = nil if self.sender_id.blank? # this coercion was done just to make activeadmin work
end
def track_user_progression
self.sender.update_progression_field(:first_invited_at) unless self.sender.nil?
end
def self.index(user)
return InvitedUser.where(:sender_id => user).order(:updated_at)
end
@ -60,7 +66,5 @@ module JamRuby
def not_accepted_twice
errors.add(:accepted, "you can only accept an invitation once") if accepted_twice
end
end
end

View File

@ -24,7 +24,7 @@ module JamRuby
after_save :require_at_least_one_genre, :limit_max_genres
after_destroy do |obj|
JamRuby::MusicSessionHistory.removed_music_session(obj.user_id, obj.id)
JamRuby::MusicSessionHistory.removed_music_session(obj.id)
end
validates :description, :presence => true, :no_profanity => true

View File

@ -67,14 +67,29 @@ module JamRuby
session_history.save!
end
def self.removed_music_session(user_id, session_id)
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(:user_id => user_id)
.where(:music_session_id => session_id)
.limit(1)
.first
hist.update_attribute(:session_removed_at, Time.now) if hist
JamRuby::MusicSessionUserHistory.removed_music_session(user_id, session_id)
hist.end_history if hist
end
def duration_minutes

View File

@ -5,11 +5,16 @@ module JamRuby
self.primary_key = 'id'
attr_accessible :max_concurrent_connections, :session_removed_at, :rating
belongs_to(:user,
:class_name => "JamRuby::User",
:foreign_key => "user_id",
:inverse_of => :music_session_user_histories)
validates_inclusion_of :rating, :in => 0..2, :allow_nil => true
after_save :track_user_progression
def music_session_history
@msh ||= JamRuby::MusicSessionHistory.find_by_music_session_id(self.music_session_id)
end
@ -41,12 +46,60 @@ module JamRuby
.where(:music_session_id => session_id)
.limit(1)
.first
hist.update_attribute(:session_removed_at, Time.now) if hist
hist.end_history if hist
end
def end_history
self.session_removed_at = Time.now if self.session_removed_at.nil?
self.update_attributes(:session_removed_at => self.session_removed_at, :max_concurrent_connections => determine_max_concurrent)
end
# figures out what the peak amount of other clients the user saw while playing at any given time.
# we use the database to get all other connections that occurred while their this user was connected,
# and then sort through them using custom logic to increase/decrease the count as people come and go
def determine_max_concurrent
overlapping_connections = MusicSessionUserHistory.where("music_session_id = ? AND
((created_at >= ? AND (session_removed_at is NULL OR session_removed_at <= ?)) OR
(created_at <= ? AND (session_removed_at is NULL OR session_removed_at >= ?)))",
self.music_session_id, self.created_at, self.session_removed_at, self.created_at, self.created_at).select('id, created_at, session_removed_at').order(:created_at)
in_out_times = []
overlapping_connections.each do |connection|
in_out_times.push([connection.created_at.nil? ? Time.at(0) : connection.created_at, true])
in_out_times.push([connection.session_removed_at.nil? ? Time.new(3000) : connection.session_removed_at, false]) #helpful to get rid of nulls for sorting
end
count_concurrent(in_out_times)
end
def count_concurrent(in_out_times)
in_out_times.sort!
max_concurrent = 0
concurrent = 0
in_out_times.each do |hit|
if hit[1]
concurrent = concurrent + 1
else
concurrent = concurrent - 1
end
if concurrent > max_concurrent
max_concurrent = concurrent
end
end
max_concurrent
end
def perf_uri
self.perf_data.try(:uri)
end
def track_user_progression
if self.rating == 0
user.update_progression_field(:first_good_music_session_at)
end
end
end
end

View File

@ -12,7 +12,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_s3_path, :photo_url, :crop_selection
# 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
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
# authorizations (for facebook, etc -- omniauth)
has_many :user_authorizations, :class_name => "JamRuby::UserAuthorization"
@ -133,6 +133,24 @@ module JamRuby
validate :email_case_insensitive_uniqueness
validate :update_email_case_insensitive_uniqueness, :if => :updating_email
def user_progression_fields
@user_progression_fields ||= Set.new ["first_downloaded_client_at", "first_ran_client_at", "first_music_session_at", "first_real_music_session_at", "first_good_music_session_at", "first_certified_gear_at", "first_invited_at", "first_friended_at", "first_social_promoted_at" ]
end
def update_progression_field(field_name, time = DateTime.now)
@updating_progression_field = true
if self[field_name].nil?
self[field_name] = time
self.save
end
end
def failed_qualification(reason)
self.last_failed_certified_gear_at = DateTime.now
self.last_failed_certified_gear_reason = reason
self.save
end
def validate_musician_instruments
errors.add(:musician_instruments, ValidationMessages::INSTRUMENT_MINIMUM_NOT_MET) if !administratively_created && musician && musician_instruments.length == 0
errors.add(:musician_instruments, ValidationMessages::INSTRUMENT_LIMIT_EXCEEDED) if !administratively_created && musician && musician_instruments.length > 5

View File

@ -0,0 +1,23 @@
require 'spec_helper'
describe Friendship do
let(:user1) { FactoryGirl.create(:user) }
let(:user2) { FactoryGirl.create(:user) }
before(:each) do
Friendship.save_using_models(user1, user2)
end
it "can create two-way friendship" do
user1.friends?(user2).should be_true
user2.friends?(user1).should be_true
end
it "should track user progression" do
user1.first_friended_at.should_not be_nil
user2.first_friended_at.should_not be_nil
end
end

View File

@ -18,6 +18,8 @@ describe InvitedUser do
invited_user.sender.should_not be_nil
invited_user.note.should be_nil
invited_user.invited_by_administrator?.should be_false
#invited_user.sender.reload
invited_user.sender.first_invited_at.should_not be_nil
end
it 'create an invitation from admin-user' do

View File

@ -0,0 +1,146 @@
require 'spec_helper'
describe MusicSessionUserHistory do
let(:some_user) { FactoryGirl.create(:user) }
let(:music_session) { FactoryGirl.create(:music_session) }
let(:history) { FactoryGirl.create(:music_session_history, :music_session => music_session) }
let(:user_history1) { FactoryGirl.create(:music_session_user_history, :history => history, :user => music_session.creator) }
let(:user_history2) { FactoryGirl.create(:music_session_user_history, :history => history, :user => some_user) }
describe "create" do
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 }
end
describe "rating" do
describe "success" do
before(:each) do
user_history1.update_attribute(:rating ,0)
end
it { user_history1.errors.any?.should be_false}
end
describe "out of range" do
before(:each) do
user_history1.update_attribute(:rating, 3)
user_history1.save
end
it { user_history1.errors.any?.should be_true}
end
end
describe "end_history" do
it "histories created at the same time" do
user_history1.reload
user_history2.reload
user_history1.end_history
user_history1.max_concurrent_connections.should == 2
end
it "history2 ends before history1 starts" do
user_history2.created_at = user_history1.created_at - 2
user_history2.session_removed_at = user_history1.created_at - 1
user_history2.save.should be_true
user_history1.reload
user_history2.reload
user_history1.end_history
user_history1.max_concurrent_connections.should == 1
end
it "history2 starts after history1 starts" do
user_history2.created_at = user_history1.created_at + 1
user_history2.session_removed_at = user_history1.created_at + 2
user_history2.save.should be_true
user_history1.reload
user_history2.reload
user_history1.end_history
user_history1.max_concurrent_connections.should == 1
end
it "history2 is within bounds of history1 " do
user_history1.session_removed_at = user_history1.created_at + 3
user_history2.created_at = user_history1.created_at + 1
user_history2.session_removed_at = user_history1.created_at + 2
user_history1.save.should be_true
user_history2.save.should be_true
user_history1.reload
user_history2.reload
user_history1.end_history
user_history1.max_concurrent_connections.should == 2
end
it "two histories with same user within bounds of history1" do
user_history3 = FactoryGirl.create(:music_session_user_history, :history => history, :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
user_history2.created_at = user_history1.created_at + 1
user_history2.session_removed_at = user_history1.created_at + 2
user_history3.created_at = user_history1.created_at + 3
user_history3.session_removed_at = user_history1.created_at + 4
user_history1.save.should be_true
user_history2.save.should be_true
user_history3.save.should be_true
user_history1.reload
user_history2.reload
user_history3.reload
user_history1.end_history
user_history1.max_concurrent_connections.should == 2
end
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 => history, :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
user_history2.created_at = user_history1.created_at + 1
user_history2.session_removed_at = user_history1.created_at + 2
user_history3.created_at = user_history1.created_at + 3
user_history3.session_removed_at = user_history1.created_at + 4
user_history1.save.should be_true
user_history2.save.should be_true
user_history3.save.should be_true
user_history1.reload
user_history2.reload
user_history3.reload
user_history1.end_history
user_history1.max_concurrent_connections.should == 2
end
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 => history, :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
user_history2.created_at = user_history1.created_at + 1
user_history2.session_removed_at = user_history1.created_at + 3
user_history3.created_at = user_history1.created_at + 2
user_history3.session_removed_at = user_history1.created_at + 4
user_history1.save.should be_true
user_history2.save.should be_true
user_history3.save.should be_true
user_history1.reload
user_history2.reload
user_history3.reload
user_history1.end_history
user_history1.max_concurrent_connections.should == 3
end
end
end

View File

@ -243,6 +243,23 @@ describe User do
its(:remember_token) { should_not be_blank }
end
describe "user progression only touches once" do
it "allows first touch" do
@user.update_progression_field (:first_downloaded_client_at)
@user.errors.any?.should be_false
@user.first_downloaded_client_at.should_not be_nil
end
it "ignores second touch" do
time = DateTime.now - 1
@user.update_progression_field(:first_downloaded_client_at, time)
first_value = @user.first_downloaded_client_at
@user.update_progression_field(:first_downloaded_client_at)
@user.errors.any?.should be_false
@user.first_downloaded_client_at.should == first_value
end
end
describe "authenticate (class-instance)" do
before { @user.email_confirmed=true; @user.save }

View File

@ -12,6 +12,7 @@
context.JK.FtueWizard = function(app) {
context.JK.FtueWizard.latencyTimeout = true;
context.JK.FtueWizard.latencyMS = Number.MAX_VALUE;
var rest = context.JK.Rest();
var logger = context.JK.logger;
var jamClient = context.jamClient;
@ -110,10 +111,14 @@
logger.debug(latencyMS + " is <= 20. Setting FTUE status to true");
ftueSave(true); // Save the profile
context.jamClient.FTUESetStatus(true); // No FTUE wizard next time
rest.userCertifiedGear({success:true});
// notify anyone curious about how it went
$('div[layout-id=ftue]').trigger('ftue_success');
}
else {
rest.userCertifiedGear({success:false, reason:"latency=" + latencyMS});
}
updateGauge();
}

View File

@ -213,6 +213,44 @@
return deferred;
}
function userDownloadedClient(options) {
return $.ajax({
type: "POST",
dataType: "json",
contentType: 'application/json',
url: "/api/users/progression/downloaded_client",
processData: false
});
}
function userCertifiedGear(options) {
return $.ajax({
type: "POST",
dataType: "json",
contentType: 'application/json',
url: "/api/users/progression/certified_gear",
processData: false,
data: JSON.stringify({
success: options.success,
reason: options.reason
})
});
}
function userSocialPromoted(options) {
var id = getId(options);
return $.ajax({
type: "POST",
dataType: "json",
contentType: 'application/json',
url: "/api/users/progression/social_promoted",
processData: false
});
}
function signout() {
return $.ajax({
type: "DELETE",
@ -245,6 +283,9 @@
this.serverHealthCheck = serverHealthCheck;
this.acceptFriendRequest = acceptFriendRequest;
this.signout = signout;
this.userDownloadedClient = userDownloadedClient;
this.userCertifiedGear = userCertifiedGear;
this.userSocialPromoted = userSocialPromoted;
return this;
};

View File

@ -43,6 +43,7 @@
var download = $(context._.template($('#client-download-link').html(), options, { variable: 'data' }));
download.find('a').data('platform', platform).click(function() {
rest.userDownloadedClient();
context.JK.GA.trackDownload($(this).data('platform'));
});

View File

@ -36,4 +36,13 @@ class ApiController < ApplicationController
@user = User.find(params[:id])
end
def optional_auth_user
if current_user.nil?
@user = nil
else
auth_user
end
end
end

View File

@ -109,6 +109,19 @@ class ApiMusicSessionsController < ApiController
respond_with @connection, responder: ApiResponder
end
def participant_rating
@history = MusicSessionUserHistory.find(params[:id])
@history.rating = params[:rating]
@history.save
if @history.errors.any?
response.status = :unprocessable_entity
respond_with @history
else
render :json => {}, :status => :ok
end
end
def lookup_session
@music_session = MusicSession.find(params[:id])
end

View File

@ -11,7 +11,6 @@ class ApiUsersController < ApiController
:notification_index, :notification_destroy, # notifications
:band_invitation_index, :band_invitation_show, :band_invitation_update, # band invitations
:set_password, :begin_update_email, :update_avatar, :delete_avatar, :generate_filepicker_policy]
respond_to :json
def index
@ -529,6 +528,48 @@ class ApiUsersController < ApiController
end
# user progression tracking
def downloaded_client
@user = current_user
@user.update_progression_field(:first_downloaded_client_at)
if @user.errors.any?
respond_with @user, :status => :unprocessable_entity
return
end
render :json => {}, :status => 200
end
# user progression tracking
def qualified_gear
@user = current_user
if params[:success]
@user.update_progression_field(:first_certified_gear_at)
else
@user.failed_qualification(params[:reason])
end
if @user.errors.any?
respond_with @user, :status => :unprocessable_entity
return
end
render :json => {}, :status => 200
end
# user progression tracking
def social_promoted
@user = current_user
@user.update_progression_field(:first_social_promoted_at)
if @user.errors.any?
respond_with @user, :status => :unprocessable_entity
return
end
render :json => {}, :status => 200
end
###################### RECORDINGS #######################
# def recording_index
# @recordings = User.recording_index(current_user, params[:id])

View File

@ -13,6 +13,11 @@ class SessionsController < ApplicationController
@login_error = true
render 'new', :layout => "landing"
else
if jkclient_agent?
user.update_progression_field(:first_ran_client_at)
end
@session_only_cookie = !jkclient_agent? && !params[:user].nil? && 0 == params[:user][:remember_me].to_i
complete_sign_in user
end

View File

@ -88,6 +88,8 @@ SampleApp::Application.routes.draw do
match '/sessions/:id/tracks/:track_id' => 'api_music_sessions#track_show', :via => :get, :as => 'api_session_track_detail'
match '/sessions/:id/tracks/:track_id' => 'api_music_sessions#track_destroy', :via => :delete
match '/participant_histories/:id/rating' => 'api_music_sessions#participant_rating', :via => :post
# genres
match '/genres' => 'api_genres#index', :via => :get
@ -174,6 +176,11 @@ SampleApp::Application.routes.draw do
match '/users/:id/avatar' => 'api_users#delete_avatar', :via => :delete
match '/users/:id/filepicker_policy' => 'api_users#generate_filepicker_policy', :via => :get
# user progression
match '/users/progression/downloaded_client' => 'api_users#downloaded_client', :via => :post
match '/users/progression/certified_gear' => 'api_users#qualified_gear', :via => :post
match '/users/progression/social_promoted' => 'api_users#social_promoted', :via => :post
# user recordings
# match '/users/:id/recordings' => 'api_users#recording_index', :via => :get
# match '/users/:id/recordings/:recording_id' => 'api_users#recording_show', :via => :get, :as => 'api_recording_detail'

View File

@ -1,5 +1,6 @@
require 'recaptcha'
class MusicSessionManager < BaseManager
class
MusicSessionManager < BaseManager
include Recaptcha::Verify

View File

@ -592,6 +592,20 @@ describe "Music Session API ", :type => :api do
music_session_perf_data.music_session_user_history.should == MusicSessionUserHistory.find_by_client_id(client.client_id)
end
it "rating" do
user = FactoryGirl.create(:user)
client = FactoryGirl.create(:connection, :user => user)
music_session = FactoryGirl.create(:music_session, :creator => user, :description => "My Session")
msuh = FactoryGirl.create(:music_session_user_history, :music_session_id => music_session.id, :client_id => client.client_id, :user_id => user.id)
msuh.rating.should be_nil
login(user)
post "/api/participant_histories/#{msuh.id}/rating.json", { :rating => 0 }.to_json, "CONTENT_TYPE" => "application/json"
last_response.status.should == 200
msuh.reload
msuh.rating.should == 0
end
end

View File

@ -0,0 +1,171 @@
require 'spec_helper'
# user progression is achieved by different aspects of the code working together in a cross-cutting fashion.
# due to this, it's nice to have a single place where all the parts of user progression are tested
# https://jamkazam.atlassian.net/wiki/pages/viewpage.action?pageId=3375145
describe "User Progression", :type => :api do
include Rack::Test::Methods
subject { page }
def login(user)
post '/sessions', "session[email]" => user.email, "session[password]" => user.password
rack_mock_session.cookie_jar["remember_token"].should == user.remember_token
end
describe "user progression" do
let(:user) { FactoryGirl.create(:user) }
let(:defopts) { { :description => "a session", :fan_chat => true, :fan_access => true, :approval_required => false, :genres => ["classical"], :musician_access => true, :tracks => [{"instrument_id" => "electric guitar", "sound" => "mono"}], :legal_terms => true, :intellectual_property => true} }
before do
login(user)
end
it "downloaded_client" do
user.first_downloaded_client_at.should be_nil
post "/api/users/progression/downloaded_client"
last_response.status.should eql(200)
JSON.parse(last_response.body).should eql({ })
user.reload
user.first_downloaded_client_at.should_not be_nil
end
it "downloaded_client twice" do
post "/api/users/progression/downloaded_client"
user.reload
first = user.first_downloaded_client_at
post "/api/users/progression/downloaded_client"
user.reload
user.first_downloaded_client_at.should == first
end
it "qualified gear" do
user.first_certified_gear_at.should be_nil
post "/api/users/progression/certified_gear.json", { :success => true}.to_json, "CONTENT_TYPE" => 'application/json'
last_response.status.should eql(200)
JSON.parse(last_response.body).should eql({ })
user.reload
user.first_certified_gear_at.should_not be_nil
user.last_failed_certified_gear_at.should be_nil
user.last_failed_certified_gear_reason.should be_nil
end
it "failed qualified gear" do
user.first_certified_gear_at.should be_nil
post "/api/users/progression/certified_gear.json", { :success => false, :reason => "latency=30"}.to_json, "CONTENT_TYPE" => 'application/json'
last_response.status.should eql(200)
JSON.parse(last_response.body).should eql({ })
user.reload
user.first_certified_gear_at.should be_nil
user.last_failed_certified_gear_at.should_not be_nil
user.last_failed_certified_gear_reason.should == "latency=30"
end
it "social promoted" do
user.first_social_promoted_at.should be_nil
post "/api/users/progression/social_promoted"
last_response.status.should eql(200)
JSON.parse(last_response.body).should eql({ })
user.reload
user.first_social_promoted_at.should_not be_nil
end
it "joined any session" do
user.first_music_session_at.should be_nil
client = FactoryGirl.create(:connection, :user => user, :ip_address => "1.1.1.1", :client_id => "1.1")
post '/api/sessions.json', defopts.merge({:client_id => client.client_id}).to_json, "CONTENT_TYPE" => 'application/json'
last_response.status.should eql(201)
user.reload
user.first_music_session_at.should_not be_nil
end
it "ran client" do
user.first_ran_client_at.should be_nil
# change user-agent to look like the real client, and see ran_client flag set
post '/sessions', {"session[email]" => user.email, "session[password]" => user.password}, { "HTTP_USER_AGENT" => " JamKazam " }
user.reload
user.first_ran_client_at.should_not be_nil
end
it "real session" do
# to make a real session, we need a session at least 15 minutes long and containting 3 concurrent users
user2 = FactoryGirl.create(:user)
user3 = FactoryGirl.create(:user)
user.first_real_music_session_at.should be_nil
client = FactoryGirl.create(:connection, :user => user, :ip_address => "1.1.1.1", :client_id => "1_1")
client2 = FactoryGirl.create(:connection, :user => user2, :ip_address => "1.1.1.2", :client_id => "1_2")
client3 = FactoryGirl.create(:connection, :user => user3, :ip_address => "1.1.1.3", :client_id => "1_3")
post '/api/sessions.json', defopts.merge({:client_id => client.client_id}).to_json, "CONTENT_TYPE" => 'application/json'
last_response.status.should eql(201)
music_session = JSON.parse(last_response.body)
login(user2)
post "/api/sessions/#{music_session["id"]}/participants.json", { :client_id => client2.client_id, :as_musician => true, :tracks => [{"instrument_id" => "bass guitar", "sound" => "mono"}]}.to_json, "CONTENT_TYPE" => 'application/json'
last_response.status.should eql(201)
login(user3)
post "/api/sessions/#{music_session["id"]}/participants.json", { :client_id => client3.client_id, :as_musician => true, :tracks => [{"instrument_id" => "bass guitar", "sound" => "mono"}]}.to_json, "CONTENT_TYPE" => 'application/json'
last_response.status.should eql(201)
# instrument the created_at of the music_history field to be at the beginning of time, so that we cross the 15 minute threshold of a 'real session
history1 = MusicSessionUserHistory.where(:user_id => user.id, :music_session_id => music_session["id"]).first
history2 = MusicSessionUserHistory.where(:user_id => user2.id, :music_session_id => music_session["id"]).first
history3 = MusicSessionUserHistory.where(:user_id => user3.id, :music_session_id => music_session["id"]).first
history1.should_not be_nil
history2.should_not be_nil
history3.should_not be_nil
history1.created_at = Time.at(0)
history2.created_at = Time.at(0)
history3.created_at = Time.at(0)
history1.save
history2.save
history3.save
# now log out of the session for all 3 users
login(user)
delete "/api/participants/#{client.client_id}.json", '', "CONTENT_TYPE" => 'application/json'
last_response.status.should eql(204)
login(user2)
delete "/api/participants/#{client2.client_id}.json", '', "CONTENT_TYPE" => 'application/json'
last_response.status.should eql(204)
login(user3)
delete "/api/participants/#{client3.client_id}.json", '', "CONTENT_TYPE" => 'application/json'
last_response.status.should eql(204)
user.reload
user2.reload
user3.reload
user.first_real_music_session_at.should_not be_nil
user2.first_real_music_session_at.should_not be_nil
user3.first_real_music_session_at.should_not be_nil
end
it "invites user" do
user.first_invited_at.should be_nil
post '/api/invited_users.json', {:email => 'tester@jamkazam.com', :note => "please join"}.to_json, "CONTENT_TYPE" => 'application/json'
last_response.status.should eql(201)
user.reload
user.first_invited_at.should_not be_nil
end
it "good session" do
user = FactoryGirl.create(:user)
user.first_good_music_session_at.should be_nil
client = FactoryGirl.create(:connection, :user => user)
music_session = FactoryGirl.create(:music_session, :creator => user, :description => "My Session")
msuh = FactoryGirl.create(:music_session_user_history, :music_session_id => music_session.id, :client_id => client.client_id, :user_id => user.id)
login(user)
post "/api/participant_histories/#{msuh.id}/rating.json", { :rating => 0 }.to_json, "CONTENT_TYPE" => "application/json"
user.reload
user.first_good_music_session_at.should_not be_nil
end
end
end