Update admin subs dashboard

Break up lengthy server calls to reduce timeouts in production.
This commit is contained in:
Matt Lott 2015-04-11 10:36:00 -07:00
parent f8beed9ce8
commit 4669d4cb0a
2 changed files with 80 additions and 44 deletions

View file

@ -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

View file

@ -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()