mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-27 01:25:42 -05:00
🐛Fix course ordering and furthest logic in dashboard
Also speeding up server APIs via lean() db calls
This commit is contained in:
parent
7efccfeac4
commit
951db5a721
5 changed files with 73 additions and 54 deletions
|
@ -157,6 +157,7 @@ block content
|
|||
.small Restricted to courses instances from last #{view.furthestCourseDayRangeRecent} days
|
||||
.small Teacher: owner of a course instance
|
||||
.small Student: member of a course instance (assigned to course)
|
||||
.small Only course.releasePhase == 'released'
|
||||
.small For course instances != Single Player, hourOfCode != true
|
||||
.small Counts are not summed. I.e. a student or teacher only contributes to the count of one course
|
||||
.small Paid student: user.coursePrepaid set and prepaid.properties.trialRequestID NOT set
|
||||
|
@ -190,7 +191,8 @@ block content
|
|||
td= row.totals['Free Students'] || 0
|
||||
td= row.totals['Total Students'] || 0
|
||||
else
|
||||
div Loading ...
|
||||
br
|
||||
p Loading ...
|
||||
|
||||
h3 Furthest Course in last #{view.furthestCourseDayRange} days
|
||||
if view.courseDistributions
|
||||
|
@ -217,7 +219,8 @@ block content
|
|||
td= row.totals['Free Students'] || 0
|
||||
td= row.totals['Total Students'] || 0
|
||||
else
|
||||
div Loading ...
|
||||
br
|
||||
p Loading ...
|
||||
|
||||
.school-sales
|
||||
h3 School Sales
|
||||
|
@ -245,7 +248,7 @@ block content
|
|||
td
|
||||
td
|
||||
else
|
||||
div Loading ...
|
||||
p Loading ...
|
||||
|
||||
#school-counts
|
||||
h3 School Counts
|
||||
|
@ -262,7 +265,7 @@ block content
|
|||
td= val.schoolName
|
||||
td= val.count
|
||||
else
|
||||
div Loading ...
|
||||
p Loading ...
|
||||
|
||||
h1 Active Users
|
||||
if view.activeUsers.length > 0
|
||||
|
|
|
@ -252,14 +252,14 @@ module.exports = class AnalyticsView extends RootView
|
|||
}, 0).load()
|
||||
|
||||
@courses = new CocoCollection([], { url: "/db/course", model: Course})
|
||||
@courses.comparator = "_id"
|
||||
@listenToOnce @courses, 'sync', @onCoursesSync
|
||||
@supermodel.loadCollection(@courses)
|
||||
|
||||
onCoursesSync: ->
|
||||
# Assumes courses retrieved in order
|
||||
@courses.remove(@courses.findWhere({releasePhase: 'beta'}))
|
||||
sortedCourses = utils.sortCourses(@courses.models ? [])
|
||||
@courseOrderMap = {}
|
||||
@courseOrderMap[@courses.models[i].get('_id')] = i for i in [0...@courses.models.length]
|
||||
@courseOrderMap[sortedCourses[i].get('_id')] = i for i in [0...sortedCourses.length]
|
||||
|
||||
startDay = new Date()
|
||||
startDay.setUTCDate(startDay.getUTCDate() - @furthestCourseDayRange)
|
||||
|
@ -291,6 +291,9 @@ module.exports = class AnalyticsView extends RootView
|
|||
for courseInstance in data.courseInstances
|
||||
continue if utils.objectIdToDate(courseInstance._id) < startDate
|
||||
courseID = courseInstance.courseID
|
||||
unless @courseOrderMap[courseID]?
|
||||
console.error "ERROR: no course order for courseID=#{courseID}"
|
||||
continue
|
||||
teacherID = courseInstance.ownerID
|
||||
for studentID in courseInstance.members
|
||||
studentPaidStatusMap[studentID] = 'free'
|
||||
|
@ -303,7 +306,7 @@ module.exports = class AnalyticsView extends RootView
|
|||
prepaidUserMap = {}
|
||||
for user in data.students
|
||||
continue unless studentPaidStatusMap[user._id]
|
||||
if prepaidID = user.coursePrepaidID or user.coursePrepaid?._id
|
||||
if prepaidID = user.coursePrepaid?._id
|
||||
studentPaidStatusMap[user._id] = 'paid'
|
||||
prepaidUserMap[prepaidID] ?= []
|
||||
prepaidUserMap[prepaidID].push(user._id)
|
||||
|
@ -324,17 +327,27 @@ module.exports = class AnalyticsView extends RootView
|
|||
teacherPaidStatusMap = {}
|
||||
for teacher, students of teacherStudentsMap
|
||||
for student in students
|
||||
unless studentFurthestCourseMap[student]?
|
||||
console.error "ERROR: no student furthest map for teacher=#{teacher} student=#{student}"
|
||||
continue
|
||||
if not teacherPaidStatusMap[teacher]
|
||||
teacherPaidStatusMap[teacher] = studentPaidStatusMap[student]
|
||||
teacherFurthestCourseMap[teacher] = studentFurthestCourseMap[student]
|
||||
else if teacherPaidStatusMap[teacher] is 'trial' and studentPaidStatusMap[student] is 'paid'
|
||||
teacherPaidStatusMap[teacher] = studentPaidStatusMap[student]
|
||||
teacherFurthestCourseMap[teacher] = studentFurthestCourseMap[student]
|
||||
else if teacherPaidStatusMap[teacher] is 'free' and studentPaidStatusMap[student] in ['paid', 'trial']
|
||||
teacherPaidStatusMap[teacher] = studentPaidStatusMap[student]
|
||||
teacherFurthestCourseMap[teacher] = studentFurthestCourseMap[student]
|
||||
else if teacherFurthestCourseMap[teacher] < studentFurthestCourseMap[student]
|
||||
teacherFurthestCourseMap[teacher] = studentFurthestCourseMap[student]
|
||||
else if teacherPaidStatusMap[teacher] is 'paid'
|
||||
if studentPaidStatusMap[student] is 'paid' and teacherFurthestCourseMap[teacher] < studentFurthestCourseMap[student]
|
||||
teacherFurthestCourseMap[teacher] = studentFurthestCourseMap[student]
|
||||
else if teacherPaidStatusMap[teacher] is 'trial'
|
||||
if studentPaidStatusMap[student] is 'paid'
|
||||
teacherPaidStatusMap[teacher] = studentPaidStatusMap[student]
|
||||
teacherFurthestCourseMap[teacher] = studentFurthestCourseMap[student]
|
||||
else if studentPaidStatusMap[student] is 'trial' and teacherFurthestCourseMap[teacher] < studentFurthestCourseMap[student]
|
||||
teacherFurthestCourseMap[teacher] = studentFurthestCourseMap[student]
|
||||
else # free teacher
|
||||
if studentPaidStatusMap[student] in ['paid', 'trial']
|
||||
teacherPaidStatusMap[teacher] = studentPaidStatusMap[student]
|
||||
teacherFurthestCourseMap[teacher] = studentFurthestCourseMap[student]
|
||||
else if studentPaidStatusMap[student] is 'free' and teacherFurthestCourseMap[teacher] < studentFurthestCourseMap[student]
|
||||
teacherFurthestCourseMap[teacher] = studentFurthestCourseMap[student]
|
||||
|
||||
# Build table of student/teacher paid/trial/free totals
|
||||
updateCourseTotalsMap = (courseTotalsMap, furthestCourseMap, paidStatusMap, columnSuffix) =>
|
||||
|
@ -360,12 +373,12 @@ module.exports = class AnalyticsView extends RootView
|
|||
courseDistributions = []
|
||||
for courseName, totals of courseTotalsMap
|
||||
courseDistributions.push({courseName: courseName, totals: totals})
|
||||
courseDistributions.sort (a, b) ->
|
||||
if a.courseName.indexOf('Introduction') >= 0 and b.courseName.indexOf('Introduction') < 0 then return -1
|
||||
else if b.courseName.indexOf('Introduction') >= 0 and a.courseName.indexOf('Introduction') < 0 then return 1
|
||||
else if a.courseName.indexOf('All Courses') >= 0 and b.courseName.indexOf('All Courses') < 0 then return 1
|
||||
courseDistributions.sort (a, b) =>
|
||||
if a.courseName.indexOf('All Courses') >= 0 and b.courseName.indexOf('All Courses') < 0 then return 1
|
||||
else if b.courseName.indexOf('All Courses') >= 0 and a.courseName.indexOf('All Courses') < 0 then return -1
|
||||
a.courseName.localeCompare(b.courseName)
|
||||
aID = @courses.findWhere({name: a.courseName}).id
|
||||
bID = @courses.findWhere({name: b.courseName}).id
|
||||
@courseOrderMap[aID] - @courseOrderMap[bID]
|
||||
|
||||
courseDistributions
|
||||
|
||||
|
|
|
@ -44,39 +44,41 @@ PaymentHandler = class PaymentHandler extends Handler
|
|||
payment
|
||||
|
||||
getSchoolSalesAPI: (req, res, code) ->
|
||||
return @sendUnauthorizedError(res) unless req.user?.isAdmin()
|
||||
userIDs = [];
|
||||
Payment.find({}, {amount: 1, created: 1, description: 1, prepaidID: 1, productID: 1, purchaser: 1, service: 1}).exec (err, payments) =>
|
||||
throw new errors.Unauthorized('You must be an administrator.') unless req.user?.isAdmin()
|
||||
|
||||
Payment.find({}, {amount: 1, created: 1, description: 1, prepaidID: 1, productID: 1, purchaser: 1, service: 1}).lean().exec (err, payments) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
userIDs = [];
|
||||
schoolSales = []
|
||||
prepaidIDs = []
|
||||
prepaidPaymentMap = {}
|
||||
for payment in payments
|
||||
continue unless payment.get('amount')? and payment.get('amount') > 0
|
||||
unless created = payment.get('created')
|
||||
created = payment.get('_id').getTimestamp()
|
||||
description = payment.get('description') ? ''
|
||||
if prepaidID = payment.get('prepaidID')
|
||||
continue unless payment.amount > 0
|
||||
unless created = payment.created
|
||||
paymentID = mongoose.Types.ObjectId(payment._id)
|
||||
created = paymentID.getTimestamp()
|
||||
description = payment.description ? ''
|
||||
if prepaidID = payment.prepaidID
|
||||
unless prepaidPaymentMap[prepaidID.valueOf()]
|
||||
prepaidPaymentMap[prepaidID.valueOf()] = {_id: payment.get('_id').valueOf(), amount: payment.get('amount'), created: created, description: description, userID: payment.get('purchaser').valueOf(), prepaidID: prepaidID.valueOf()}
|
||||
prepaidPaymentMap[prepaidID.valueOf()] = {_id: payment._id, amount: payment.amount, created: created, description: description, userID: payment.purchaser, prepaidID: prepaidID}
|
||||
prepaidIDs.push(prepaidID)
|
||||
userIDs.push(payment.get('purchaser'))
|
||||
else if payment.get('productID') is 'custom' or payment.get('service') is 'external' or payment.get('service') is 'invoice'
|
||||
schoolSales.push({_id: payment.get('_id').valueOf(), amount: payment.get('amount'), created: created, description: description, userID: payment.get('purchaser').valueOf()})
|
||||
userIDs.push(payment.get('purchaser'))
|
||||
userIDs.push(payment.purchaser)
|
||||
else if payment.productID is 'custom' or payment.service is 'external' or payment.service is 'invoice'
|
||||
schoolSales.push({_id: payment._id, amount: payment.amount, created: created, description: description, userID: payment.purchaser})
|
||||
userIDs.push(payment.purchaser)
|
||||
|
||||
Prepaid.find({$and: [{_id: {$in: prepaidIDs}}, {type: 'course'}]}, {_id: 1}).exec (err, prepaids) =>
|
||||
Prepaid.find({$and: [{_id: {$in: prepaidIDs}}, {type: 'course'}]}, {_id: 1}).lean().exec (err, prepaids) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
for prepaid in prepaids
|
||||
schoolSales.push(prepaidPaymentMap[prepaid.get('_id').valueOf()])
|
||||
schoolSales.push(prepaidPaymentMap[prepaid._id])
|
||||
|
||||
User.find({_id: {$in: userIDs}}).exec (err, users) =>
|
||||
User.find({_id: {$in: userIDs}}).lean().exec (err, users) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
userMap = {}
|
||||
for user in users
|
||||
userMap[user.get('_id').valueOf()] = user
|
||||
userMap[user._id] = user
|
||||
for schoolSale in schoolSales
|
||||
schoolSale.user = userMap[schoolSale.userID]?.toObject()
|
||||
schoolSale.user = userMap[schoolSale.userID]
|
||||
|
||||
@sendSuccess(res, schoolSales)
|
||||
|
||||
|
|
|
@ -477,11 +477,11 @@ UserHandler = class UserHandler extends Handler
|
|||
{schoolName: {$exists: true}},
|
||||
{schoolName: {$ne: ''}}
|
||||
]}
|
||||
User.find(query, {schoolName: 1}).exec (err, documents) =>
|
||||
User.find(query, {schoolName: 1}).lean().exec (err, documents) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
schoolCountMap = {}
|
||||
for doc in documents
|
||||
schoolName = doc.get('schoolName')
|
||||
schoolName = doc.schoolName
|
||||
schoolCountMap[schoolName] ?= 0;
|
||||
schoolCountMap[schoolName]++;
|
||||
schoolCounts = []
|
||||
|
@ -489,7 +489,6 @@ UserHandler = class UserHandler extends Handler
|
|||
continue unless count >= minCount
|
||||
schoolCounts.push schoolName: schoolName, count: count
|
||||
@sendSuccess(res, schoolCounts)
|
||||
|
||||
agreeToCLA: (req, res) ->
|
||||
return @sendForbiddenError(res) unless req.user
|
||||
doc =
|
||||
|
|
|
@ -155,27 +155,29 @@ module.exports =
|
|||
|
||||
|
||||
fetchRecent: wrap (req, res) ->
|
||||
query = {$and: [{name: {$ne: 'Single Player'}}, {hourOfCode: {$ne: true}}]}
|
||||
throw new errors.Unauthorized('You must be an administrator.') unless req.user?.isAdmin()
|
||||
|
||||
courses = yield Course.find({releasePhase: 'released'}).select({_id: 1}).lean()
|
||||
courseIDs = (course._id for course in courses)
|
||||
|
||||
query = {$and: [{courseID: {$in: courseIDs}}, {name: {$ne: 'Single Player'}}, {hourOfCode: {$ne: true}}]}
|
||||
query["$and"].push(_id: {$gte: objectIdFromTimestamp(req.body.startDay + "T00:00:00.000Z")}) if req.body.startDay?
|
||||
query["$and"].push(_id: {$lt: objectIdFromTimestamp(req.body.endDay + "T00:00:00.000Z")}) if req.body.endDay?
|
||||
courseInstances = yield CourseInstance.find(query, {courseID: 1, members: 1, ownerID: 1})
|
||||
courseInstances = yield CourseInstance.find(query, {courseID: 1, members: 1, ownerID: 1}).lean()
|
||||
|
||||
userIDs = []
|
||||
for courseInstance in courseInstances
|
||||
if members = courseInstance.get('members')
|
||||
if members = courseInstance.members
|
||||
userIDs.push(userID) for userID in members
|
||||
users = yield User.find({_id: {$in: userIDs}}, {coursePrepaid: 1, coursePrepaidID: 1})
|
||||
users = yield User.find({_id: {$in: userIDs}, coursePrepaid: {$exists: true}}, {coursePrepaid: 1}).lean()
|
||||
|
||||
prepaidIDs = []
|
||||
for user in users
|
||||
if prepaidID = user.get('coursePrepaid')
|
||||
prepaidIDs.push(prepaidID._id)
|
||||
prepaids = yield Prepaid.find({_id: {$in: prepaidIDs}}, {properties: 1})
|
||||
prepaidIDs = (user.coursePrepaid._id for user in users when user.coursePrepaid)
|
||||
prepaids = yield Prepaid.find({_id: {$in: prepaidIDs}}, {properties: 1}).lean()
|
||||
|
||||
res.send({
|
||||
courseInstances: (courseInstance.toObject({req: req}) for courseInstance in courseInstances)
|
||||
students: (user.toObject({req: req}) for user in users)
|
||||
prepaids: (prepaid.toObject({req: req}) for prepaid in prepaids)
|
||||
courseInstances: courseInstances
|
||||
students: users
|
||||
prepaids: prepaids
|
||||
})
|
||||
|
||||
fetchNonHoc: wrap (req, res) ->
|
||||
|
|
Loading…
Reference in a new issue