ARS vs P2P, subscription fixes, no block on max time

This commit is contained in:
Seth Call 2021-01-16 19:37:34 -06:00
parent ba22761222
commit 1262b9fd60
13 changed files with 198 additions and 42 deletions

View File

@ -74,6 +74,12 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
redirect_to :back, {notice: "Check the Subscription Plan Code, Subscription Sync Code, Subscription Sync Msg"}
end
member_action :reset_monthly_play, :method => :get do
resource.used_month_play_time = 0
resource.save!
redirect_to :back, {notice: "Reset user's monthly play time to 0"}
end
member_action :change_to_plan, :method => :get do
@client = RecurlyClient.new
plan_code = params[:plan_code]
@ -128,6 +134,7 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
row :gender
row :email_confirmed
row :remember_token
=begin
row "Session Ready" do |user|
div do
if user.ready_for_session_at
@ -154,6 +161,7 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
end
end
end
=end
row "Delete Forever" do |user|
span do
link_to("delete forever", delete_forever_admin_user_path(user.id), :data => {:confirm => 'Are you sure?'})
@ -201,6 +209,24 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
row :subscription_sync_msg
row :is_past_due
row :stored_credit_card
row "Monthly Time Used" do |user|
div do
remaining_month_play_time = user.subscription_rules[:remaining_month_play_time]
if remaining_month_play_time.nil?
span do
"No limit"
end
elsif user.played_this_month?
span do
"Used: #{user.used_month_play_time / 60} min | Remaining #{remaining_month_play_time / 60} min"
end
else
span do
"Did not play this month. Last played #{user.used_current_month}"
end
end
end
end
end
end
div do
@ -225,9 +251,19 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
'sets secret override to give user a free plan (link goes to another page)'
end
div do
link_to("Give No-Payment Plan", edit_admin_user_override_path(user.id))
link_to("give no-payment plan", edit_admin_user_override_path(user.id))
end
end
div do
h3 do
'Reset Monthly Play Time'
end
h4 do
'sets the user\'s monthly play time to 0'
end
div do
link_to("reset monthly play time", reset_monthly_play_admin_user_path(user.id), :data => {:confirm => 'Are you sure?'})
end
end
div do
h3 do
@ -331,6 +367,7 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
end
end
=begin
panel "Teacher Setting" do
attributes_table do
@ -396,6 +433,8 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
end
=end
panel "JamTracks" do
div do
link_to "Give JamTrack", "../jam_track_rights/new"

View File

@ -181,6 +181,16 @@ SQL
clients
end
# deletes any connections in active music sessions older than 2 days.
def cleanup_dangling
ConnectionManager.active_record_transaction do |connection_manager, conn|
sql = "update connections set music_session_id = null where id in (select id from connections where music_session_id in (select id from active_music_sessions where updated_at::date < (current_date - 2)))"
conn.exec(sql) do |result|
end
end
end
# returns the number of connections that this user currently has across all clients
# this number is used by notification logic elsewhere to know

View File

@ -98,7 +98,7 @@ module JamRuby
# update the users monthly play time
def update_remaining_play_time
now = Time.now
current_month = (now.year * 100 + now.month)
current_month = User.current_month
remaining_time_so_far = self.user.used_month_play_time.to_i
if current_month != self.user.used_current_month
@ -117,7 +117,7 @@ module JamRuby
# 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
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)

View File

@ -2879,12 +2879,11 @@ module JamRuby
if play_time_per_month.nil?
rules[:remaining_month_play_time] = nil
else
now = Time.now
if used_current_month != (now.year * 100 + now.month)
if played_this_month?
rules[:remaining_month_play_time] = (play_time_per_month * 3600) - self.used_month_play_time.to_i
else
# if this is a new month, then they get full play time
rules[:remaining_month_play_time] = (play_time_per_month * 3600)
else
rules[:remaining_month_play_time] = (play_time_per_month * 3600) - self.used_month_play_time.to_i
end
end
end
@ -2892,6 +2891,15 @@ module JamRuby
rules
end
def self.current_month
now = Time.now
(now.year * 100 + now.month)
end
def played_this_month?
used_current_month == User.current_month
end
def update_admin_override_plan_code(plan_code)
self.admin_override_plan_code = plan_code
self.subscription_plan_code = plan_code

View File

@ -64,6 +64,11 @@ module JamRuby
if subscription
puts "Canceling user's #{current_user.email} subscription"
subscription.cancel
# upon cancelation, take the user's current monthly payment plan
subscription = Recurly::Subscription.find(subscription.uuid)
current_user.subscription_plan_code = get_highest_plan(subscription)
current_user.subscription_plan_code_set_at = DateTime.now
current_user.save(validate: false)
# do not delete the recurly_subscription_id ; we'll use that to try and reactivate later if they user re-activates their account
else
# if no subscription and past trial, you goin down -- because there must have never been payment??
@ -323,12 +328,24 @@ module JamRuby
raise RecurlyClientError.new(plan.errors) if plan.errors.any?
end
def get_pending_plan_code(subscription)
if subscription && subscription.pending_subscription
return subscription.pending_subscription.plan.plan_code
else
return nil
end
end
def get_highest_plan(subscription)
SubscriptionDefinitions.higher_plan(subscription.plan.plan_code, get_pending_plan_code(subscription))
end
def handle_create_subscription(current_user, plan_code, account)
begin
subscription = create_subscription(current_user, plan_code, account, current_user.subscription_trial_ended? ? nil : current_user.subscription_trial_ends_at)
current_user.recurly_subscription_id = subscription.uuid
if current_user.subscription_trial_ended?
current_user.subscription_plan_code = plan_code
current_user.subscription_plan_code = get_highest_plan(subscription)
current_user.subscription_plan_code_set_at = DateTime.now
else
# we could force a platinum plan since the user has put forward payment already, even in trial
@ -364,13 +381,14 @@ module JamRuby
begin
old_subscription.reactivate
puts "reactivated plan! Let's check if it needs changing"
if plan_code != old_subscription.plan.plan_code
#if plan_code != old_subscription.plan.plan_code
result = old_subscription.update_attributes(
:plan_code => plan_code,
:timeframe => starts_at.nil? ? 'bill_date' : 'now'
)
end
return old_subscription
# end
# fetch it again. because it's got staleness after update_attributes operation
return Recurly::Subscription.find(old_subscription_id)
rescue => e
puts "Unable to reactivate/update old plan #{e}"
user.update_attribute(:recurly_subscription_id, nil)

View File

@ -12,6 +12,7 @@ module JamRuby
#LessonSession.hourly_check
#TeacherPayment.hourly_check
User.hourly_check
ConnectionManager.new.cleanup_dangling
@@log.info("done")
end

View File

@ -117,5 +117,22 @@ module JamRuby
next_plan_rank < current_plan_rank
end
def self.higher_plan(plan_a, plan_b)
if plan_a.nil?
plan_b
elsif plan_b.nil?
plan_a
else
plan_a_rank = rules(plan_a)[:rank]
plan_b_rank = rules(plan_b)[:rank]
# if plan a is higher, take plan_a
if plan_a_rank > plan_b_rank
plan_a
else
plan_b
end
end
end
end
end

View File

@ -11,7 +11,7 @@ describe ConnectionManager, no_transaction: true do
GATEWAY = 'gateway1'
REACHABLE = true
let(:channel_id) {'1'}
let(:channel_id) { '1' }
before do
@conn = PG::Connection.new(:dbname => SpecDb::TEST_DB_NAME, :user => "postgres", :password => "postgres", :host => "localhost")
@ -42,6 +42,32 @@ describe ConnectionManager, no_transaction: true do
end
end
describe "cleanup_dangling" do
it "success" do
@connman.cleanup_dangling
client_id = "client_id9"
user_id = create_user("test", "user9", "user9@jamkazam.com")
music_session = FactoryGirl.create(:active_music_session, user_id: user_id)
music_session_id = music_session.id
user = User.find(user_id)
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME, REACHABLE, GATEWAY, false)
connection = @connman.join_music_session(user, client_id, music_session, true, TRACKS, 10)
Connection.where('music_session_id is not null').count.should == 1
@connman.cleanup_dangling
Connection.where('music_session_id is not null').count.should == 1
# well in the past
music_session.update_attribute("updated_at", '2020-01-01 00:00:00')
@connman.cleanup_dangling
Connection.where('music_session_id is not null').count.should == 0
end
end
it "can't create two client_ids of same value" do
client_id = "client_id1"
user_id = create_user("test", "user1", "user1@jamkazam.com")
@ -263,7 +289,7 @@ describe ConnectionManager, no_transaction: true do
user_id = create_user("test", "user8", "user8@jamkazam.com")
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME, REACHABLE, GATEWAY, false)
num = JamRuby::Connection.where('aasm_state = ?','connected').count
num = JamRuby::Connection.where('aasm_state = ?', 'connected').count
num.should == 1
assert_num_connections(client_id, num)
@connman.flag_stale_connections(GATEWAY)
@ -346,7 +372,6 @@ describe ConnectionManager, no_transaction: true do
end
it "join_music_session fails if no connection" do
client_id = "client_id10"
@ -429,7 +454,7 @@ describe ConnectionManager, no_transaction: true do
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME, REACHABLE, GATEWAY, false)
# specify real user id, but not associated with this session
expect { @connman.join_music_session(user, client_id, music_session, true, TRACKS, 10) } .to raise_error(JamRuby::JamPermissionError)
expect { @connman.join_music_session(user, client_id, music_session, true, TRACKS, 10) }.to raise_error(JamRuby::JamPermissionError)
end
it "join_music_session fails if no music_session" do
@ -455,7 +480,7 @@ describe ConnectionManager, no_transaction: true do
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME, REACHABLE, GATEWAY, false)
# specify real user id, but not associated with this session
expect { @connman.join_music_session(user, client_id, music_session, true, TRACKS, 10) } .to raise_error(JamRuby::JamPermissionError)
expect { @connman.join_music_session(user, client_id, music_session, true, TRACKS, 10) }.to raise_error(JamRuby::JamPermissionError)
end

View File

@ -205,6 +205,9 @@ AppStore = context.AppStore
displayTime: (until_time) ->
if until_time < 0
return 'no time'
untilTime = @getTimeRemaining(until_time * 1000)
timeString = ''
@ -213,9 +216,9 @@ AppStore = context.AppStore
if untilTime.hours != 0 || timeString.length > 0
timeString += "#{untilTime.hours} hours, "
if untilTime.minutes != 0 || timeString.length > 0
timeString += "#{untilTime.minutes} minutes, "
if untilTime.seconds != 0 || timeString.length > 0
timeString += "#{untilTime.seconds} seconds"
timeString += "#{untilTime.minutes} minutes "
#if untilTime.seconds != 0 || timeString.length > 0
# timeString += "#{untilTime.seconds} seconds"
if timeString == ''
'now!'
@ -295,15 +298,18 @@ AppStore = context.AppStore
explanation = `<span>You have successfully upgraded your plan to the {desired_plan_name} level, thank you</span>`
warning = `<p className="uncollectable-msg">However, you must provide a payment method (e.g. a credit card), for the monthly subscription charge. Please click the Update Payment Method button to do this now.</p>`
else
explanation = `<span>You have successfully upgraded your plan to the {desired_plan_name} level, thank you!</span>`
explanation = `<span>You are currently on the {effective_plan_name} level, thank you!</span>`
else
# free plan situation - not much to go on about
explanation = `<span>You are currently on the {desired_plan_name} plan.</span>`
explanation = `<span>You are currently on the {effective_plan_name} plan.</span>`
if show_payment_info
update_payment_btn = `<a className="button-orange update-payment-method" href="/client#/account/paymentHistory" onClick={this.onUpdatePaymentMethod}>UPDATE PAYMENT METHOD</a>`
if has_pending_subscription
billingAddendum = null #`<span><br/><br/><span>You will be billed next at the <span className="plan-name">{this.getDisplayNameTier(this.props.subscription.subscription.plan.plan_code)}</span> on the next billing cycle.</span></span>`
if this.props.subscription.subscription.plan.plan_code != this.props.subscription.plan_code
billingAddendum = `<span>You have paid only for the <span className="plan-name">{this.getDisplayNameTier(this.props.subscription.subscription.plan.plan_code)}</span> level for the current billing cycle, so there will be a change to the <span className="plan-name">{this.getDisplayNameTier(this.props.subscription.subscription.pending_subscription.plan.plan_code)}</span> level on the next billing cycle.</span>`
else
billingAddendum = `<span>And your plan and billing will switch to the <span className="plan-name">{this.getDisplayNameTier(this.props.subscription.subscription.pending_subscription.plan.plan_code)}</span> level on the next billing cycle.</span>`
else if cancelled_subscription && this.props.subscription.desired_plan_code == null && this.props.subscription.plan_code != null
billingAddendum = `<span>However, your cancelled {effective_plan_name} plan is still active until the end of the billing cycle.</span>`# `<span><br/><br/><span>You will be billed a final time at the <span className="plan-name">{this.getDisplayNameTier(this.props.subscription.subscription.plan.plan_code)}</span> at end of this billing cycle.</span></span>`
else
@ -315,7 +321,7 @@ AppStore = context.AppStore
#until_time.setSeconds(until_time.getSeconds() + @subscriptionRules.remaining_month_play_time)
playtime = `<div className="play-time">
<label>monthly play time remaining:</label>
<p>You have <span className="playtime">{this.displayTime(remaining_month_play_time)}</span> time remaining this month. Only the time you spend in a session with 2 or more people uses your session play time.</p>
<p>You have <span className="playtime">{this.displayTime(remaining_month_play_time)}</span> remaining this month. Only the time you spend in a session with 2 or more people uses your session play time.</p>
</div>`
else
playtime = `<div className="play-time">

View File

@ -39,15 +39,20 @@ StatsInfo = {
poor: (user, stats) -> "You have not enough bandwidth to send you a decent quality audio stream.",
},
video_rtpbw_rx: {
good: (user, stats) -> "#{user.name} have enough bandwidth to send you a high quality video stream.",
warn: (user, stats) -> "#{user.name} have enough bandwidth to send you a degraded, but sufficient, video stream.",
poor: (user, stats) -> "#{user.name} have not enough bandwidth to send you a decent quality video stream.",
good: (user, stats) -> "#{user.name} has enough bandwidth to send you a high quality video stream.",
warn: (user, stats) -> "#{user.name} has enough bandwidth to send you a degraded, but sufficient, video stream.",
poor: (user, stats) -> "#{user.name} has not enough bandwidth to send you a decent quality video stream.",
},
video_rtpbw_tx: {
good: (user, stats) -> "You have enough bandwidth to send you a high quality video stream.",
warn: (user, stats) -> "You have enough bandwidth to send you a degraded, but sufficient, video stream.",
poor: (user, stats) -> "You have not enough bandwidth to send you a decent quality video stream.",
},
ars_vs_p2p: {
good: (user, stats) -> "Your network path is also the lowest latency path available",
warn: (user, stats) -> "Your network path is not the lowest latency path available",
poor: (user, stats) -> "Your network path is not the lowest latency path available",
},
ping: {
good: (user, stats) -> "The internet connection between you and #{user.name} has very low latency.",
warn: (user, stats) -> "The internet connection between you and #{user.name} has average latency, which may affect staying in sync.",
@ -204,6 +209,8 @@ StatsInfo = {
networkStats.push(@stat(network, 'network', 'Video Bw Rx', 'video_rtpbw_rx', Math.round(network.video_rtpbw_rx) + ' k'))
if network.video_rtpbw_tx?
networkStats.push(@stat(network, 'network', 'Video Bw Tx', 'video_rtpbw_tx', Math.round(network.video_rtpbw_tx) + ' k'))
if network.ars_vs_p2p?
networkStats.push(@stat(network, 'network', 'Net Path', 'ars_vs_p2p', network.ars_vs_p2p))
networkTag =
`<div className="network-stats stats-holder">

View File

@ -112,6 +112,20 @@ AggregateThresholds = SessionStatThresholds.aggregate
total_latency += network.audiojq_median * 2.5
aggregate.one_way = network.ping / 2
aggregate.jq = network.audiojq_median * 2.5
if network.using_ars_path == 0
network.ars_vs_p2p = 'P2P'
if network.preferred_path == 'p2p'
network.ars_vs_p2p_level = 'good'
else
network.ars_vs_p2p_level = 'warn'
else
network.ars_vs_p2p = 'ARS'
if network.preferred_path == 'p2p'
network.ars_vs_p2p_level = 'warn'
else
network.ars_vs_p2p_level = 'good'
console.log("NETWORK!", network)
else
total_latency = null

View File

@ -422,7 +422,7 @@
&.SessionStatsHover {
width:385px;
height:615px;
height:645px;
@include border_box_sizing;
h3 {

View File

@ -328,6 +328,28 @@ class ApiMusicSessionsController < ApiController
end
begin
@subscription_rules = current_user.subscription_rules
@music_session = ActiveMusicSession.find_by_id(params[:id])
if @music_session && @music_session.users.count >= 1
if @music_session.users.count == 1 && @music_session.users.first.id == current_user.id
# if somehow we find ourselves in the session, just ignore that case.
else
@session_rules = { remaining_session_play_time: @music_session.play_time_remaining(current_user) }
# check if the user has gone past acceptable play time
if !@session_rules[:remaining_session_play_time].nil? && @session_rules[:remaining_session_play_time] <= 0
# user has no session time for this session left.
render :json => { :errors => {:remaining_session_play_time => ['none remaining']}}, :status => 422
return
elsif !@subscription_rules[:remaining_month_play_time].nil? && @subscription_rules[:remaining_month_play_time] <= 0
# user has no session time this month.
render :json => { :errors => {:remaining_month_play_time=> ['none remaining']}}, :status => 422
return
end
end
end
@connection = ActiveMusicSession.participant_create(
current_user,
params[:id],
@ -345,23 +367,12 @@ class ApiMusicSessionsController < ApiController
respond_with @connection
else
@music_session = @connection.music_session
if @session_rules.nil?
@session_rules = { remaining_session_play_time: @music_session.play_time_remaining(current_user) }
end
# used in rabl to render extra data
@on_join = true
@subscription_rules = current_user.subscription_rules
@session_rules = { remaining_session_play_time: @music_session.play_time_remaining(current_user) }
# check if the user has gone past acceptable play time
if !@session_rules[:remaining_session_play_time].nil? && @session_rules[:remaining_session_play_time] <= 0
# user has no session time for this session left.
render :json => { :errors => {:remaining_session_play_time => ['none remaining']}}, :status => 422
return
elsif !@subscription_rules[:remaining_month_play_time].nil? && @subscription_rules[:remaining_month_play_time] <= 0
# user has no session time this month.
render :json => { :errors => {:remaining_month_play_time=> ['none remaining']}}, :status => 422
return
end
respond_with @music_session, responder: ApiResponder, :status => 201, :location => api_session_detail_url(@connection.music_session)
end