mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-04-29 15:35:15 -04:00
Update admin subs dashboard
Break up lengthy server calls to reduce timeouts in production.
This commit is contained in:
parent
f8beed9ce8
commit
4669d4cb0a
2 changed files with 80 additions and 44 deletions
|
@ -47,7 +47,20 @@ module.exports = class AnalyticsSubscriptionsView extends RootView
|
||||||
return unless me.isAdmin()
|
return unless me.isAdmin()
|
||||||
@resetSubscriptionsData()
|
@resetSubscriptionsData()
|
||||||
@getSubscribers()
|
@getSubscribers()
|
||||||
@getSubscriptions()
|
@getCancellations (cancellations) =>
|
||||||
|
@getSubscriptions(cancellations)
|
||||||
|
|
||||||
|
getCancellations: (done) ->
|
||||||
|
options =
|
||||||
|
url: '/db/subscription/-/cancellations'
|
||||||
|
method: 'GET'
|
||||||
|
options.error = (model, response, options) =>
|
||||||
|
return if @destroyed
|
||||||
|
console.error 'Failed to get cancellations', response
|
||||||
|
options.success = (cancellations, response, options) =>
|
||||||
|
return if @destroyed
|
||||||
|
done(cancellations)
|
||||||
|
@supermodel.addRequestResource('get_cancellations', options, 0).load()
|
||||||
|
|
||||||
getSubscribers: ->
|
getSubscribers: ->
|
||||||
options =
|
options =
|
||||||
|
@ -67,7 +80,7 @@ module.exports = class AnalyticsSubscriptionsView extends RootView
|
||||||
@render?()
|
@render?()
|
||||||
@supermodel.addRequestResource('get_subscribers', options, 0).load()
|
@supermodel.addRequestResource('get_subscribers', options, 0).load()
|
||||||
|
|
||||||
getSubscriptions: ->
|
getSubscriptions: (cancellations=[]) ->
|
||||||
options =
|
options =
|
||||||
url: '/db/subscription/-/subscriptions'
|
url: '/db/subscription/-/subscriptions'
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
|
@ -83,14 +96,18 @@ module.exports = class AnalyticsSubscriptionsView extends RootView
|
||||||
subDayMap[startDay] ?= {}
|
subDayMap[startDay] ?= {}
|
||||||
subDayMap[startDay]['start'] ?= 0
|
subDayMap[startDay]['start'] ?= 0
|
||||||
subDayMap[startDay]['start']++
|
subDayMap[startDay]['start']++
|
||||||
if cancelDay = sub?.cancel?.substring(0, 10)
|
|
||||||
subDayMap[cancelDay] ?= {}
|
|
||||||
subDayMap[cancelDay]['cancel'] ?= 0
|
|
||||||
subDayMap[cancelDay]['cancel']++
|
|
||||||
if endDay = sub?.end?.substring(0, 10)
|
if endDay = sub?.end?.substring(0, 10)
|
||||||
subDayMap[endDay] ?= {}
|
subDayMap[endDay] ?= {}
|
||||||
subDayMap[endDay]['end'] ?= 0
|
subDayMap[endDay]['end'] ?= 0
|
||||||
subDayMap[endDay]['end']++
|
subDayMap[endDay]['end']++
|
||||||
|
for cancellation in cancellations
|
||||||
|
if cancellation.subID is sub.subID
|
||||||
|
cancelDay = cancellation.cancel.substring(0, 10)
|
||||||
|
subDayMap[cancelDay] ?= {}
|
||||||
|
subDayMap[cancelDay]['cancel'] ?= 0
|
||||||
|
subDayMap[cancelDay]['cancel']++
|
||||||
|
break
|
||||||
|
|
||||||
today = new Date().toISOString().substring(0, 10)
|
today = new Date().toISOString().substring(0, 10)
|
||||||
for day of subDayMap
|
for day of subDayMap
|
||||||
continue if day > today
|
continue if day > today
|
||||||
|
|
|
@ -24,10 +24,53 @@ class SubscriptionHandler extends Handler
|
||||||
console.warn "Subscription Error: #{user.get('slug')} (#{user._id}): '#{msg}'"
|
console.warn "Subscription Error: #{user.get('slug')} (#{user._id}): '#{msg}'"
|
||||||
|
|
||||||
getByRelationship: (req, res, args...) ->
|
getByRelationship: (req, res, args...) ->
|
||||||
|
return @getCancellations(req, res) if args[1] is 'cancellations'
|
||||||
return @getSubscribers(req, res) if args[1] is 'subscribers'
|
return @getSubscribers(req, res) if args[1] is 'subscribers'
|
||||||
return @getSubscriptions(req, res) if args[1] is 'subscriptions'
|
return @getSubscriptions(req, res) if args[1] is 'subscriptions'
|
||||||
super(arguments...)
|
super(arguments...)
|
||||||
|
|
||||||
|
getCancellations: (req, res) =>
|
||||||
|
return @sendForbiddenError(res) unless req.user and req.user.isAdmin()
|
||||||
|
@cancellations = []
|
||||||
|
nextBatch = (starting_after, done) =>
|
||||||
|
options = limit: 100
|
||||||
|
options.starting_after = starting_after if starting_after
|
||||||
|
stripe.customers.list options, (err, customers) =>
|
||||||
|
return done(err) if err
|
||||||
|
|
||||||
|
for customer in customers.data
|
||||||
|
continue unless customer?.subscriptions?.data?.length > 0
|
||||||
|
for subscription in customer.subscriptions.data
|
||||||
|
continue unless subscription.plan.id is 'basic'
|
||||||
|
|
||||||
|
amount = subscription.plan.amount
|
||||||
|
if subscription?.discount?.coupon?
|
||||||
|
if subscription.discount.coupon.percent_off
|
||||||
|
amount = amount * (100 - subscription.discount.coupon.percent_off) / 100;
|
||||||
|
else if subscription.discount.coupon.amount_off
|
||||||
|
amount -= subscription.discount.coupon.amount_off
|
||||||
|
else if customer.discount?.coupon?
|
||||||
|
if customer.discount.coupon.percent_off
|
||||||
|
amount = amount * (100 - customer.discount.coupon.percent_off) / 100
|
||||||
|
else if customer.discount.coupon.amount_off
|
||||||
|
amount -= customer.discount.coupon.amount_off
|
||||||
|
|
||||||
|
continue unless amount > 0
|
||||||
|
|
||||||
|
if subscription.cancel_at_period_end
|
||||||
|
@cancellations.push
|
||||||
|
cancel: new Date(subscription.canceled_at * 1000)
|
||||||
|
subID: subscription.id
|
||||||
|
|
||||||
|
if customers.has_more
|
||||||
|
# console.log 'Fetching more customers', Object.keys(@cancellations).length
|
||||||
|
return nextBatch(customers.data[customers.data.length - 1].id, done)
|
||||||
|
else
|
||||||
|
return done()
|
||||||
|
nextBatch null, (err) =>
|
||||||
|
return @sendDatabaseError(res, err) if err
|
||||||
|
@sendSuccess(res, @cancellations)
|
||||||
|
|
||||||
getSubscribers: (req, res) ->
|
getSubscribers: (req, res) ->
|
||||||
return @sendForbiddenError(res) unless req.user and req.user.isAdmin()
|
return @sendForbiddenError(res) unless req.user and req.user.isAdmin()
|
||||||
|
|
||||||
|
@ -102,8 +145,6 @@ class SubscriptionHandler extends Handler
|
||||||
# return @sendSuccess(res, @subs) unless _.isEmpty(@subs)
|
# return @sendSuccess(res, @subs) unless _.isEmpty(@subs)
|
||||||
@subMap = {}
|
@subMap = {}
|
||||||
|
|
||||||
console.log 'Fetching invoices...'
|
|
||||||
|
|
||||||
processInvoices = (starting_after, done) =>
|
processInvoices = (starting_after, done) =>
|
||||||
options = limit: 100
|
options = limit: 100
|
||||||
options.starting_after = starting_after if starting_after
|
options.starting_after = starting_after if starting_after
|
||||||
|
@ -124,49 +165,27 @@ class SubscriptionHandler extends Handler
|
||||||
last: invoiceDate
|
last: invoiceDate
|
||||||
customerID: invoice.customer
|
customerID: invoice.customer
|
||||||
if invoices.has_more
|
if invoices.has_more
|
||||||
console.log 'Fetching more invoices', Object.keys(@subMap).length
|
# console.log 'Fetching more invoices', Object.keys(@subMap).length
|
||||||
return processInvoices(invoices.data[invoices.data.length - 1].id, done)
|
return processInvoices(invoices.data[invoices.data.length - 1].id, done)
|
||||||
else
|
else
|
||||||
return done()
|
return done()
|
||||||
|
|
||||||
processInvoices null, (err) =>
|
processInvoices null, (err) =>
|
||||||
return @sendDatabaseError(res, err) if err
|
return @sendDatabaseError(res, err) if err
|
||||||
|
@subs = []
|
||||||
console.log 'Checking cancelled subscriptions...'
|
|
||||||
|
|
||||||
createCheckCancelledFn = (customerID, subscriptionID) =>
|
|
||||||
(done) =>
|
|
||||||
stripe.customers.retrieveSubscription customerID, subscriptionID, (err, subscription) =>
|
|
||||||
return done() if err
|
|
||||||
if subscription?.cancel_at_period_end
|
|
||||||
@subMap[subscriptionID].cancel = new Date(subscription.canceled_at * 1000)
|
|
||||||
done()
|
|
||||||
|
|
||||||
tasks = []
|
|
||||||
for subID of @subMap
|
for subID of @subMap
|
||||||
expectedLastPayment = new Date(@subMap[subID].last)
|
sub =
|
||||||
expectedLastPayment.setUTCFullYear(new Date().getUTCFullYear())
|
start: @subMap[subID].first
|
||||||
expectedLastPayment.setUTCMonth(new Date().getUTCMonth() - 1)
|
subID: subID
|
||||||
expectedLastPayment.setUTCDate(new Date().getUTCDate() - 8) # In case last payment had some retries
|
customerID: @subMap[subID].customerID
|
||||||
if @subMap[subID].last > expectedLastPayment
|
sub.cancel = @subMap[subID].cancel if @subMap[subID].cancel
|
||||||
tasks.push createCheckCancelledFn(@subMap[subID].customerID, subID)
|
oneMonthAgo = new Date()
|
||||||
|
oneMonthAgo.setUTCMonth(oneMonthAgo.getUTCMonth() - 1)
|
||||||
async.parallel tasks, (err, results) =>
|
if @subMap[subID].last < oneMonthAgo
|
||||||
return @sendDatabaseError(res, err) if err
|
sub.end = new Date(@subMap[subID].last)
|
||||||
@subs = []
|
sub.end.setUTCMonth(sub.end.getUTCMonth() + 1)
|
||||||
for subID of @subMap
|
@subs.push sub
|
||||||
sub =
|
@sendSuccess(res, @subs)
|
||||||
start: @subMap[subID].first
|
|
||||||
subID: subID
|
|
||||||
customerID: @subMap[subID].customerID
|
|
||||||
sub.cancel = @subMap[subID].cancel if @subMap[subID].cancel
|
|
||||||
oneMonthAgo = new Date()
|
|
||||||
oneMonthAgo.setUTCMonth(oneMonthAgo.getUTCMonth() - 1)
|
|
||||||
if @subMap[subID].last < oneMonthAgo
|
|
||||||
sub.end = new Date(@subMap[subID].last)
|
|
||||||
sub.end.setUTCMonth(sub.end.getUTCMonth() + 1)
|
|
||||||
@subs.push sub
|
|
||||||
@sendSuccess(res, @subs)
|
|
||||||
|
|
||||||
subscribeUser: (req, user, done) ->
|
subscribeUser: (req, user, done) ->
|
||||||
if (not req.user) or req.user.isAnonymous() or user.isAnonymous()
|
if (not req.user) or req.user.isAnonymous() or user.isAnonymous()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue