mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-12-13 09:11:22 -05:00
Merge branch 'master' into production
This commit is contained in:
commit
dbc99ba52b
8 changed files with 275 additions and 251 deletions
|
@ -267,7 +267,7 @@ LevelSchema = c.object {
|
||||||
victory: {}
|
victory: {}
|
||||||
type: 'hero'
|
type: 'hero'
|
||||||
goals: [
|
goals: [
|
||||||
{id: 'ogres-die', name: 'Ogres must die.', killThangs: ['ogres'], worldEndsAfter: 3}
|
{id: 'ogres-die', name: 'Defeat the ogres.', killThangs: ['ogres'], worldEndsAfter: 3}
|
||||||
{id: 'humans-survive', name: 'Your hero must survive.', saveThangs: ['Hero Placeholder'], howMany: 1, worldEndsAfter: 3, hiddenGoal: true}
|
{id: 'humans-survive', name: 'Your hero must survive.', saveThangs: ['Hero Placeholder'], howMany: 1, worldEndsAfter: 3, hiddenGoal: true}
|
||||||
]
|
]
|
||||||
concepts: ['basic_syntax']
|
concepts: ['basic_syntax']
|
||||||
|
|
|
@ -267,9 +267,9 @@ block content
|
||||||
h1 Active Users
|
h1 Active Users
|
||||||
if view.activeUsers.length > 0
|
if view.activeUsers.length > 0
|
||||||
- var eventNames = [];
|
- var eventNames = [];
|
||||||
each count, event in view.activeUsers[0].events
|
each event in view.activeUserEventNames
|
||||||
if event.indexOf('classroom') >= 0
|
if event.indexOf('classroom') >= 0
|
||||||
- eventNames.push(event)
|
- eventNames.push(event);
|
||||||
- eventNames.sort(function (a, b) {return a.localeCompare(b);});
|
- eventNames.sort(function (a, b) {return a.localeCompare(b);});
|
||||||
table.table.table-striped.table-condensed
|
table.table.table-striped.table-condensed
|
||||||
tr
|
tr
|
||||||
|
@ -322,9 +322,9 @@ block content
|
||||||
h1 Active Users
|
h1 Active Users
|
||||||
if view.activeUsers.length > 0
|
if view.activeUsers.length > 0
|
||||||
- var eventNames = [];
|
- var eventNames = [];
|
||||||
each count, event in view.activeUsers[0].events
|
each event in view.activeUserEventNames
|
||||||
if event.indexOf('campaign') >= 0
|
if event.indexOf('campaign') >= 0
|
||||||
- eventNames.push(event)
|
- eventNames.push(event);
|
||||||
- eventNames.sort(function (a, b) {return a.localeCompare(b);});
|
- eventNames.sort(function (a, b) {return a.localeCompare(b);});
|
||||||
table.table.table-striped.table-condensed
|
table.table.table-striped.table-condensed
|
||||||
tr
|
tr
|
||||||
|
@ -346,27 +346,13 @@ block content
|
||||||
|
|
||||||
h1 Active Users
|
h1 Active Users
|
||||||
if view.activeUsers.length > 0
|
if view.activeUsers.length > 0
|
||||||
- var eventNames = [];
|
|
||||||
each count, event in view.activeUsers[0].events
|
|
||||||
- eventNames.push(event)
|
|
||||||
- eventNames.sort(function (a, b) {
|
|
||||||
- if (a.indexOf('campaign') == b.indexOf('campaign') || a.indexOf('classroom') == b.indexOf('classroom')) {
|
|
||||||
- return a.localeCompare(b);
|
|
||||||
- }
|
|
||||||
- else if (a.indexOf('campaign') > b.indexOf('campaign')) {
|
|
||||||
- return 1;
|
|
||||||
- }
|
|
||||||
- else {
|
|
||||||
- return -1;
|
|
||||||
- }
|
|
||||||
- });
|
|
||||||
table.table.table-striped.table-condensed
|
table.table.table-striped.table-condensed
|
||||||
tr
|
tr
|
||||||
th(style='min-width:85px;') Day
|
th(style='min-width:85px;') Day
|
||||||
each eventName in eventNames
|
each eventName in view.activeUserEventNames
|
||||||
th= eventName
|
th= eventName
|
||||||
each activeUser in view.activeUsers
|
each activeUser in view.activeUsers
|
||||||
tr
|
tr
|
||||||
td= activeUser.day
|
td= activeUser.day
|
||||||
each eventName in eventNames
|
each eventName in view.activeUserEventNames
|
||||||
td= activeUser.events[eventName] || 0
|
td= activeUser.events[eventName] || 0
|
||||||
|
|
|
@ -73,6 +73,7 @@ module.exports = class AnalyticsView extends RootView
|
||||||
# Add campaign/classroom DAU 30-day averages and daily totals
|
# Add campaign/classroom DAU 30-day averages and daily totals
|
||||||
campaignDauTotals = []
|
campaignDauTotals = []
|
||||||
classroomDauTotals = []
|
classroomDauTotals = []
|
||||||
|
eventMap = {}
|
||||||
for entry in @activeUsers
|
for entry in @activeUsers
|
||||||
day = entry.day
|
day = entry.day
|
||||||
campaignDauTotal = 0
|
campaignDauTotal = 0
|
||||||
|
@ -82,18 +83,31 @@ module.exports = class AnalyticsView extends RootView
|
||||||
campaignDauTotal += count
|
campaignDauTotal += count
|
||||||
else if event.indexOf('DAU classroom') >= 0
|
else if event.indexOf('DAU classroom') >= 0
|
||||||
classroomDauTotal += count
|
classroomDauTotal += count
|
||||||
|
eventMap[event] = true;
|
||||||
entry.events['DAU campaign total'] = campaignDauTotal
|
entry.events['DAU campaign total'] = campaignDauTotal
|
||||||
|
eventMap['DAU campaign total'] = true;
|
||||||
campaignDauTotals.unshift(campaignDauTotal)
|
campaignDauTotals.unshift(campaignDauTotal)
|
||||||
campaignDauTotals.pop() while campaignDauTotals.length > 30
|
campaignDauTotals.pop() while campaignDauTotals.length > 30
|
||||||
if campaignDauTotals.length is 30
|
if campaignDauTotals.length is 30
|
||||||
entry.events['DAU campaign 30-day average'] = Math.round(_.reduce(campaignDauTotals, (a, b) -> a + b) / 30)
|
entry.events['DAU campaign 30-day average'] = Math.round(_.reduce(campaignDauTotals, (a, b) -> a + b) / 30)
|
||||||
|
eventMap['DAU campaign 30-day average'] = true;
|
||||||
entry.events['DAU classroom total'] = classroomDauTotal
|
entry.events['DAU classroom total'] = classroomDauTotal
|
||||||
|
eventMap['DAU classroom total'] = true;
|
||||||
classroomDauTotals.unshift(classroomDauTotal)
|
classroomDauTotals.unshift(classroomDauTotal)
|
||||||
classroomDauTotals.pop() while classroomDauTotals.length > 30
|
classroomDauTotals.pop() while classroomDauTotals.length > 30
|
||||||
if classroomDauTotals.length is 30
|
if classroomDauTotals.length is 30
|
||||||
entry.events['DAU classroom 30-day average'] = Math.round(_.reduce(classroomDauTotals, (a, b) -> a + b) / 30)
|
entry.events['DAU classroom 30-day average'] = Math.round(_.reduce(classroomDauTotals, (a, b) -> a + b) / 30)
|
||||||
|
eventMap['DAU classroom 30-day average'] = true;
|
||||||
|
|
||||||
@activeUsers.sort (a, b) -> b.day.localeCompare(a.day)
|
@activeUsers.sort (a, b) -> b.day.localeCompare(a.day)
|
||||||
|
@activeUserEventNames = Object.keys(eventMap)
|
||||||
|
@activeUserEventNames.sort (a, b) ->
|
||||||
|
if a.indexOf('campaign') is b.indexOf('campaign') or a.indexOf('classroom') is b.indexOf('classroom')
|
||||||
|
a.localeCompare(b)
|
||||||
|
else if a.indexOf('campaign') > b.indexOf('campaign')
|
||||||
|
1
|
||||||
|
else
|
||||||
|
-1
|
||||||
|
|
||||||
@updateAllKPIChartData()
|
@updateAllKPIChartData()
|
||||||
@updateActiveUsersChartData()
|
@updateActiveUsersChartData()
|
||||||
|
@ -183,7 +197,7 @@ module.exports = class AnalyticsView extends RootView
|
||||||
@supermodel.addRequestResource({
|
@supermodel.addRequestResource({
|
||||||
url: '/db/prepaid/-/courses'
|
url: '/db/prepaid/-/courses'
|
||||||
method: 'POST'
|
method: 'POST'
|
||||||
data: {project: {maxRedeemers: 1, properties: 1, redeemers: 1}}
|
data: {project: {endDate: 1, maxRedeemers: 1, properties: 1, redeemers: 1}}
|
||||||
success: (prepaids) =>
|
success: (prepaids) =>
|
||||||
paidDayMaxMap = {}
|
paidDayMaxMap = {}
|
||||||
paidDayRedeemedMap = {}
|
paidDayRedeemedMap = {}
|
||||||
|
@ -201,14 +215,13 @@ module.exports = class AnalyticsView extends RootView
|
||||||
redeemDay = redeemer.date.substring(0, 10)
|
redeemDay = redeemer.date.substring(0, 10)
|
||||||
trialDayRedeemedMap[redeemDay] ?= 0
|
trialDayRedeemedMap[redeemDay] ?= 0
|
||||||
trialDayRedeemedMap[redeemDay]++
|
trialDayRedeemedMap[redeemDay]++
|
||||||
else
|
else if not prepaid.endDate? or new Date(prepaid.endDate) > new Date()
|
||||||
paidDayMaxMap[day] ?= 0
|
paidDayMaxMap[day] ?= 0
|
||||||
paidDayMaxMap[day] += prepaid.maxRedeemers
|
paidDayMaxMap[day] += prepaid.maxRedeemers
|
||||||
for redeemer in prepaid.redeemers
|
for redeemer in prepaid.redeemers
|
||||||
redeemDay = redeemer.date.substring(0, 10)
|
redeemDay = redeemer.date.substring(0, 10)
|
||||||
paidDayRedeemedMap[redeemDay] ?= 0
|
paidDayRedeemedMap[redeemDay] ?= 0
|
||||||
paidDayRedeemedMap[redeemDay]++
|
paidDayRedeemedMap[redeemDay]++
|
||||||
|
|
||||||
@dayEnrollmentsMap = {}
|
@dayEnrollmentsMap = {}
|
||||||
@paidCourseTotalEnrollments = []
|
@paidCourseTotalEnrollments = []
|
||||||
for day, count of paidDayMaxMap
|
for day, count of paidDayMaxMap
|
||||||
|
@ -368,14 +381,17 @@ module.exports = class AnalyticsView extends RootView
|
||||||
|
|
||||||
# Trim points preceding days
|
# Trim points preceding days
|
||||||
if points.length and days.length and points[0].day.localeCompare(days[0]) < 0
|
if points.length and days.length and points[0].day.localeCompare(days[0]) < 0
|
||||||
for point, i in points
|
if points[points.length - 1].day.localeCompare(days[0]) < 0
|
||||||
if point.day.localeCompare(days[0]) >= 0
|
points = []
|
||||||
points.splice(0, i)
|
else
|
||||||
break
|
for point, i in points
|
||||||
|
if point.day.localeCompare(days[0]) >= 0
|
||||||
|
points.splice(0, i)
|
||||||
|
break
|
||||||
|
|
||||||
# Ensure points for each day
|
# Ensure points for each day
|
||||||
for day, i in days
|
for day, i in days
|
||||||
if points.length <= i or points[i].day isnt day
|
if points.length <= i or points[i]?.day isnt day
|
||||||
prevY = if i > 0 then points[i - 1].y else 0.0
|
prevY = if i > 0 then points[i - 1].y else 0.0
|
||||||
points.splice i, 0,
|
points.splice i, 0,
|
||||||
day: day
|
day: day
|
||||||
|
@ -648,16 +664,14 @@ module.exports = class AnalyticsView extends RootView
|
||||||
dailyMax = 0
|
dailyMax = 0
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
total = 0
|
|
||||||
for entry in @paidCourseTotalEnrollments
|
for entry in @paidCourseTotalEnrollments
|
||||||
total += entry.count
|
|
||||||
data.push
|
data.push
|
||||||
day: entry.day
|
day: entry.day
|
||||||
value: total
|
value: entry.count
|
||||||
points = @createLineChartPoints(days, data)
|
points = @createLineChartPoints(days, data)
|
||||||
@enrollmentsChartLines.push
|
@enrollmentsChartLines.push
|
||||||
points: points
|
points: points
|
||||||
description: 'Total paid enrollments issued'
|
description: 'Paid enrollments issued'
|
||||||
lineColor: @lineColors[colorIndex++ % @lineColors.length]
|
lineColor: @lineColors[colorIndex++ % @lineColors.length]
|
||||||
strokeWidth: 1
|
strokeWidth: 1
|
||||||
min: 0
|
min: 0
|
||||||
|
@ -666,16 +680,14 @@ module.exports = class AnalyticsView extends RootView
|
||||||
dailyMax = _.max([dailyMax, _.max(points, 'y').y])
|
dailyMax = _.max([dailyMax, _.max(points, 'y').y])
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
total = 0
|
|
||||||
for entry in @paidCourseRedeemedEnrollments
|
for entry in @paidCourseRedeemedEnrollments
|
||||||
total += entry.count
|
|
||||||
data.push
|
data.push
|
||||||
day: entry.day
|
day: entry.day
|
||||||
value: total
|
value: entry.count
|
||||||
points = @createLineChartPoints(days, data)
|
points = @createLineChartPoints(days, data)
|
||||||
@enrollmentsChartLines.push
|
@enrollmentsChartLines.push
|
||||||
points: points
|
points: points
|
||||||
description: 'Total paid enrollments redeemed'
|
description: 'Paid enrollments redeemed'
|
||||||
lineColor: @lineColors[colorIndex++ % @lineColors.length]
|
lineColor: @lineColors[colorIndex++ % @lineColors.length]
|
||||||
strokeWidth: 1
|
strokeWidth: 1
|
||||||
min: 0
|
min: 0
|
||||||
|
@ -684,16 +696,14 @@ module.exports = class AnalyticsView extends RootView
|
||||||
dailyMax = _.max([dailyMax, _.max(points, 'y').y])
|
dailyMax = _.max([dailyMax, _.max(points, 'y').y])
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
total = 0
|
|
||||||
for entry in @trialCourseTotalEnrollments
|
for entry in @trialCourseTotalEnrollments
|
||||||
total += entry.count
|
|
||||||
data.push
|
data.push
|
||||||
day: entry.day
|
day: entry.day
|
||||||
value: total
|
value: entry.count
|
||||||
points = @createLineChartPoints(days, data)
|
points = @createLineChartPoints(days, data, true)
|
||||||
@enrollmentsChartLines.push
|
@enrollmentsChartLines.push
|
||||||
points: points
|
points: points
|
||||||
description: 'Total trial enrollments issued'
|
description: 'Trial enrollments issued'
|
||||||
lineColor: @lineColors[colorIndex++ % @lineColors.length]
|
lineColor: @lineColors[colorIndex++ % @lineColors.length]
|
||||||
strokeWidth: 1
|
strokeWidth: 1
|
||||||
min: 0
|
min: 0
|
||||||
|
@ -702,16 +712,14 @@ module.exports = class AnalyticsView extends RootView
|
||||||
dailyMax = _.max([dailyMax, _.max(points, 'y').y])
|
dailyMax = _.max([dailyMax, _.max(points, 'y').y])
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
total = 0
|
|
||||||
for entry in @trialCourseRedeemedEnrollments
|
for entry in @trialCourseRedeemedEnrollments
|
||||||
total += entry.count
|
|
||||||
data.push
|
data.push
|
||||||
day: entry.day
|
day: entry.day
|
||||||
value: total
|
value: entry.count
|
||||||
points = @createLineChartPoints(days, data)
|
points = @createLineChartPoints(days, data)
|
||||||
@enrollmentsChartLines.push
|
@enrollmentsChartLines.push
|
||||||
points: points
|
points: points
|
||||||
description: 'Total trial enrollments redeemed'
|
description: 'Trial enrollments redeemed'
|
||||||
lineColor: @lineColors[colorIndex++ % @lineColors.length]
|
lineColor: @lineColors[colorIndex++ % @lineColors.length]
|
||||||
strokeWidth: 1
|
strokeWidth: 1
|
||||||
min: 0
|
min: 0
|
||||||
|
|
|
@ -31,31 +31,10 @@ module.exports = class MainAdminView extends RootView
|
||||||
getTitle: -> return $.i18n.t('account_settings.admin')
|
getTitle: -> return $.i18n.t('account_settings.admin')
|
||||||
|
|
||||||
initialize: ->
|
initialize: ->
|
||||||
@campaigns = new Campaigns()
|
|
||||||
@courses = new CocoCollection([], { url: "/db/course", model: Course})
|
|
||||||
|
|
||||||
if window.amActually
|
if window.amActually
|
||||||
@amActually = new User({_id: window.amActually})
|
@amActually = new User({_id: window.amActually})
|
||||||
@amActually.fetch()
|
@amActually.fetch()
|
||||||
@supermodel.trackModel(@amActually)
|
@supermodel.trackModel(@amActually)
|
||||||
if me.isAdmin()
|
|
||||||
@supermodel.trackRequest @campaigns.fetchByType('course', { data: { project: 'levels' } })
|
|
||||||
@supermodel.loadCollection(@courses, 'courses')
|
|
||||||
super()
|
|
||||||
|
|
||||||
onLoaded: ->
|
|
||||||
campaignCourseIndexMap = {}
|
|
||||||
for course, index in @courses.models
|
|
||||||
campaignCourseIndexMap[course.get('campaignID')] = index + 1
|
|
||||||
@courseLevels = []
|
|
||||||
for campaign in @campaigns.models
|
|
||||||
continue unless campaignCourseIndexMap[campaign.id]
|
|
||||||
for levelID, level of campaign.get('levels')
|
|
||||||
@courseLevels.push({
|
|
||||||
levelID
|
|
||||||
slug: level.slug
|
|
||||||
courseIndex: campaignCourseIndexMap[campaign.id]
|
|
||||||
})
|
|
||||||
super()
|
super()
|
||||||
|
|
||||||
onClickStopSpyingButton: ->
|
onClickStopSpyingButton: ->
|
||||||
|
@ -157,42 +136,31 @@ module.exports = class MainAdminView extends RootView
|
||||||
@supermodel.addRequestResource('create_prepaid', options, 0).load()
|
@supermodel.addRequestResource('create_prepaid', options, 0).load()
|
||||||
|
|
||||||
onClickExportProgress: ->
|
onClickExportProgress: ->
|
||||||
return unless @courseLevels?.length > 0
|
|
||||||
$('.classroom-progress-csv').prop('disabled', true)
|
$('.classroom-progress-csv').prop('disabled', true)
|
||||||
|
|
||||||
classCode = $('.classroom-progress-class-code').val()
|
classCode = $('.classroom-progress-class-code').val()
|
||||||
|
classroom = null
|
||||||
|
courseLevels = []
|
||||||
|
sessions = null
|
||||||
|
users = null
|
||||||
userMap = {}
|
userMap = {}
|
||||||
new Promise((resolve, reject) =>
|
Promise.resolve(new Classroom().fetchByCode(classCode))
|
||||||
new Classroom().fetchByCode(classCode, {
|
.then (model) =>
|
||||||
success: resolve
|
classroom = new Classroom({ _id: model.data._id })
|
||||||
error: (model, response, options) => reject(response)
|
Promise.resolve(classroom.fetch())
|
||||||
})
|
.then (model) =>
|
||||||
)
|
for course, index in classroom.get('courses')
|
||||||
.then (classroom) =>
|
for level in course.levels
|
||||||
new Promise((resolve, reject) =>
|
courseLevels.push
|
||||||
new Classroom({ _id: classroom.id }).fetch({
|
courseIndex: index + 1
|
||||||
success: resolve
|
levelID: level.original
|
||||||
error: (model, response, options) => reject(response)
|
slug: level.slug
|
||||||
})
|
users = new Users()
|
||||||
)
|
Promise.resolve($.when(users.fetchForClassroom(classroom)...))
|
||||||
.then (classroom) =>
|
.then (models) =>
|
||||||
new Promise((resolve, reject) =>
|
|
||||||
new Users().fetchForClassroom(classroom, {
|
|
||||||
success: (models, response, options) =>
|
|
||||||
resolve([classroom, models]) if models?.loaded
|
|
||||||
error: (models, response, options) => reject(response)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.then ([classroom, users]) =>
|
|
||||||
userMap[user.id] = user for user in users.models
|
userMap[user.id] = user for user in users.models
|
||||||
new Promise((resolve, reject) =>
|
sessions = new LevelSessions()
|
||||||
new LevelSessions().fetchForAllClassroomMembers(classroom, {
|
Promise.resolve($.when(sessions.fetchForAllClassroomMembers(classroom)...))
|
||||||
success: (models, response, options) =>
|
.then (models) =>
|
||||||
resolve(models) if models?.loaded
|
|
||||||
error: (models, response, options) => reject(response)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.then (sessions) =>
|
|
||||||
userLevelPlaytimeMap = {}
|
userLevelPlaytimeMap = {}
|
||||||
for session in sessions.models
|
for session in sessions.models
|
||||||
continue unless session.get('state')?.complete
|
continue unless session.get('state')?.complete
|
||||||
|
@ -205,7 +173,7 @@ module.exports = class MainAdminView extends RootView
|
||||||
userPlaytimes = []
|
userPlaytimes = []
|
||||||
for userID, user of userMap
|
for userID, user of userMap
|
||||||
playtimes = [user.get('name') ? 'Anonymous']
|
playtimes = [user.get('name') ? 'Anonymous']
|
||||||
for level in @courseLevels
|
for level in courseLevels
|
||||||
if userLevelPlaytimeMap[userID]?[level.levelID]?
|
if userLevelPlaytimeMap[userID]?[level.levelID]?
|
||||||
rawSeconds = parseInt(userLevelPlaytimeMap[userID][level.levelID])
|
rawSeconds = parseInt(userLevelPlaytimeMap[userID][level.levelID])
|
||||||
hours = Math.floor(rawSeconds / 60 / 60)
|
hours = Math.floor(rawSeconds / 60 / 60)
|
||||||
|
@ -222,7 +190,7 @@ module.exports = class MainAdminView extends RootView
|
||||||
columnLabels = "Username"
|
columnLabels = "Username"
|
||||||
currentLevel = 1
|
currentLevel = 1
|
||||||
lastCourseIndex = 1
|
lastCourseIndex = 1
|
||||||
for level in @courseLevels
|
for level in courseLevels
|
||||||
unless level.courseIndex is lastCourseIndex
|
unless level.courseIndex is lastCourseIndex
|
||||||
currentLevel = 1
|
currentLevel = 1
|
||||||
lastCourseIndex = level.courseIndex
|
lastCourseIndex = level.courseIndex
|
||||||
|
@ -238,3 +206,4 @@ module.exports = class MainAdminView extends RootView
|
||||||
.catch (error) ->
|
.catch (error) ->
|
||||||
$('.classroom-progress-csv').prop('disabled', false)
|
$('.classroom-progress-csv').prop('disabled', false)
|
||||||
console.error error
|
console.error error
|
||||||
|
throw error
|
||||||
|
|
|
@ -11,7 +11,7 @@ module.exports = class SegmentCheckView extends CocoView
|
||||||
events:
|
events:
|
||||||
'click .back-to-account-type': -> @trigger 'nav-back'
|
'click .back-to-account-type': -> @trigger 'nav-back'
|
||||||
'input .class-code-input': 'onInputClassCode'
|
'input .class-code-input': 'onInputClassCode'
|
||||||
'input .birthday-form-group': 'onInputBirthday'
|
'change .birthday-form-group': 'onInputBirthday'
|
||||||
'submit form.segment-check': 'onSubmitSegmentCheck'
|
'submit form.segment-check': 'onSubmitSegmentCheck'
|
||||||
'click .individual-path-button': -> @trigger 'choose-path', 'individual'
|
'click .individual-path-button': -> @trigger 'choose-path', 'individual'
|
||||||
|
|
||||||
|
|
|
@ -275,78 +275,77 @@ module.exports = class SpellView extends CocoView
|
||||||
e.editor.execCommand 'gotolineend'
|
e.editor.execCommand 'gotolineend'
|
||||||
return true
|
return true
|
||||||
|
|
||||||
if me.level() < 20 or aceConfig.indentGuides
|
# Add visual indent guides
|
||||||
# Add visual ident guides
|
language = @spell.language
|
||||||
language = @spell.language
|
ensureLineStartsBlock = (line) ->
|
||||||
ensureLineStartsBlock = (line) ->
|
return false unless language is "python"
|
||||||
return false unless language is "python"
|
match = /^\s*([^#]+)/.exec(line)
|
||||||
match = /^\s*([^#]+)/.exec(line)
|
return false if not match?
|
||||||
return false if not match?
|
return /:\s*$/.test(match[1])
|
||||||
return /:\s*$/.test(match[1])
|
|
||||||
|
|
||||||
@aceSession.addDynamicMarker
|
@aceSession.addDynamicMarker
|
||||||
update: (html, markerLayer, session, config) =>
|
update: (html, markerLayer, session, config) =>
|
||||||
Range = ace.require('ace/range').Range
|
Range = ace.require('ace/range').Range
|
||||||
|
|
||||||
foldWidgets = @aceSession.foldWidgets
|
foldWidgets = @aceSession.foldWidgets
|
||||||
return if not foldWidgets?
|
return if not foldWidgets?
|
||||||
|
|
||||||
lines = @aceDoc.getAllLines()
|
lines = @aceDoc.getAllLines()
|
||||||
startOfRow = (r) ->
|
startOfRow = (r) ->
|
||||||
str = lines[r]
|
str = lines[r]
|
||||||
ar = str.match(/^\s*/)
|
ar = str.match(/^\s*/)
|
||||||
ar.pop().length
|
ar.pop().length
|
||||||
|
|
||||||
colors = [{border: '74,144,226', fill: '108,162,226'}, {border: '132,180,235', fill: '230,237,245'}]
|
colors = [{border: '74,144,226', fill: '108,162,226'}, {border: '132,180,235', fill: '230,237,245'}]
|
||||||
|
|
||||||
for row in [0..@aceSession.getLength()]
|
for row in [0..@aceSession.getLength()]
|
||||||
foldWidgets[row] = @aceSession.getFoldWidget(row) unless foldWidgets[row]?
|
foldWidgets[row] = @aceSession.getFoldWidget(row) unless foldWidgets[row]?
|
||||||
continue unless foldWidgets? and foldWidgets[row] is "start"
|
continue unless foldWidgets? and foldWidgets[row] is "start"
|
||||||
try
|
try
|
||||||
docRange = @aceSession.getFoldWidgetRange(row)
|
docRange = @aceSession.getFoldWidgetRange(row)
|
||||||
catch error
|
catch error
|
||||||
console.warn "Couldn't find fold widget docRange for row #{row}:", error
|
console.warn "Couldn't find fold widget docRange for row #{row}:", error
|
||||||
if not docRange?
|
if not docRange?
|
||||||
guess = startOfRow(row)
|
guess = startOfRow(row)
|
||||||
docRange = new Range(row,guess,row,guess+4)
|
docRange = new Range(row,guess,row,guess+4)
|
||||||
|
|
||||||
continue unless ensureLineStartsBlock(lines[row])
|
continue unless ensureLineStartsBlock(lines[row])
|
||||||
|
|
||||||
if /^\s+$/.test lines[docRange.end.row+1]
|
if /^\s+$/.test lines[docRange.end.row+1]
|
||||||
docRange.end.row += 1
|
docRange.end.row += 1
|
||||||
|
|
||||||
xstart = startOfRow(row)
|
xstart = startOfRow(row)
|
||||||
if language is 'python'
|
if language is 'python'
|
||||||
requiredIndent = new RegExp '^' + new Array(Math.floor(xstart / 4 + 1)).join('( |\t)') + '( |\t)+(\\S|\\s*$)'
|
requiredIndent = new RegExp '^' + new Array(Math.floor(xstart / 4 + 1)).join('( |\t)') + '( |\t)+(\\S|\\s*$)'
|
||||||
for crow in [docRange.start.row+1..docRange.end.row]
|
for crow in [docRange.start.row+1..docRange.end.row]
|
||||||
unless requiredIndent.test lines[crow]
|
unless requiredIndent.test lines[crow]
|
||||||
docRange.end.row = crow - 1
|
docRange.end.row = crow - 1
|
||||||
break
|
break
|
||||||
|
|
||||||
rstart = @aceSession.documentToScreenPosition docRange.start.row, docRange.start.column
|
rstart = @aceSession.documentToScreenPosition docRange.start.row, docRange.start.column
|
||||||
rend = @aceSession.documentToScreenPosition docRange.end.row, docRange.end.column
|
rend = @aceSession.documentToScreenPosition docRange.end.row, docRange.end.column
|
||||||
range = new Range rstart.row, rstart.column, rend.row, rend.column
|
range = new Range rstart.row, rstart.column, rend.row, rend.column
|
||||||
level = Math.floor(xstart / 4)
|
level = Math.floor(xstart / 4)
|
||||||
color = colors[level % colors.length]
|
color = colors[level % colors.length]
|
||||||
bw = 3
|
bw = 3
|
||||||
to = markerLayer.$getTop(range.start.row, config)
|
to = markerLayer.$getTop(range.start.row, config)
|
||||||
t = markerLayer.$getTop(range.start.row + 1, config)
|
t = markerLayer.$getTop(range.start.row + 1, config)
|
||||||
h = config.lineHeight * (range.end.row - range.start.row)
|
h = config.lineHeight * (range.end.row - range.start.row)
|
||||||
l = markerLayer.$padding + xstart * config.characterWidth
|
l = markerLayer.$padding + xstart * config.characterWidth
|
||||||
# w = (data.i - data.b) * config.characterWidth
|
# w = (data.i - data.b) * config.characterWidth
|
||||||
w = 4 * config.characterWidth
|
w = 4 * config.characterWidth
|
||||||
fw = config.characterWidth * ( @aceSession.getScreenLastRowColumn(range.start.row) - xstart )
|
fw = config.characterWidth * ( @aceSession.getScreenLastRowColumn(range.start.row) - xstart )
|
||||||
|
|
||||||
html.push """
|
html.push """
|
||||||
<div style=
|
<div style=
|
||||||
"position: absolute; top: #{to}px; left: #{l}px; width: #{fw+bw}px; height: #{config.lineHeight}px;
|
"position: absolute; top: #{to}px; left: #{l}px; width: #{fw+bw}px; height: #{config.lineHeight}px;
|
||||||
border: #{bw}px solid rgba(#{color.border},1); border-left: none;"
|
border: #{bw}px solid rgba(#{color.border},1); border-left: none;"
|
||||||
></div>
|
></div>
|
||||||
<div style=
|
<div style=
|
||||||
"position: absolute; top: #{t}px; left: #{l}px; width: #{w}px; height: #{h}px; background-color: rgba(#{color.fill},0.5);
|
"position: absolute; top: #{t}px; left: #{l}px; width: #{w}px; height: #{h}px; background-color: rgba(#{color.fill},0.5);
|
||||||
border-right: #{bw}px solid rgba(#{color.border},1); border-bottom: #{bw}px solid rgba(#{color.border},1);"
|
border-right: #{bw}px solid rgba(#{color.border},1); border-bottom: #{bw}px solid rgba(#{color.border},1);"
|
||||||
></div>
|
></div>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
fillACE: ->
|
fillACE: ->
|
||||||
@ace.setValue @spell.source
|
@ace.setValue @spell.source
|
||||||
|
|
|
@ -6,51 +6,49 @@
|
||||||
// Usage:
|
// Usage:
|
||||||
// mongo <address>:<port>/<database> <script file> -u <username> -p <password>
|
// mongo <address>:<port>/<database> <script file> -u <username> -p <password>
|
||||||
|
|
||||||
try {
|
// TODO: Does not handle course prepaid updates on a user
|
||||||
var logDB = new Mongo("localhost").getDB("analytics")
|
// TODO: Does not handle class membership changes
|
||||||
var scriptStartTime = new Date();
|
|
||||||
var analyticsStringCache = {};
|
|
||||||
|
|
||||||
var minClassSize = 12;
|
// TODO: Investigate abrupt trial drop off at 4/1/16. Showed up when fixing coursePrepaid.
|
||||||
var minActiveCount = 6;
|
|
||||||
|
|
||||||
var eventNamePaid = 'Active classes paid';
|
var analyticsDB = new Mongo("localhost").getDB("analytics")
|
||||||
var eventNameTrial = 'Active classes trial';
|
var scriptStartTime = new Date();
|
||||||
var eventNameFree = 'Active classes free';
|
var analyticsStringCache = {};
|
||||||
|
|
||||||
var numDays = 40;
|
var minClassSize = 12;
|
||||||
var daysInMonth = 30;
|
var minActiveCount = 6;
|
||||||
|
|
||||||
var startDay = new Date();
|
var eventNamePaid = 'Active classes paid';
|
||||||
var today = startDay.toISOString().substr(0, 10);
|
var eventNameTrial = 'Active classes trial';
|
||||||
startDay.setUTCDate(startDay.getUTCDate() - numDays);
|
var eventNameFree = 'Active classes free';
|
||||||
startDay = startDay.toISOString().substr(0, 10);
|
|
||||||
|
|
||||||
log("Today is " + today);
|
var numDays = 40;
|
||||||
log("Start day is " + startDay);
|
var daysInMonth = 30;
|
||||||
|
|
||||||
log("Getting active class counts..");
|
var startDay = new Date();
|
||||||
var activeClassCounts = getActiveClassCounts(startDay);
|
var today = startDay.toISOString().substr(0, 10);
|
||||||
// printjson(activeClassCounts);
|
startDay.setUTCDate(startDay.getUTCDate() - numDays);
|
||||||
log("Inserting active class counts..");
|
startDay = startDay.toISOString().substr(0, 10);
|
||||||
for (var event in activeClassCounts) {
|
|
||||||
for (var day in activeClassCounts[event]) {
|
log("Today is " + today);
|
||||||
if (today === day) continue; // Never save data for today because it's incomplete
|
log("Start day is " + startDay);
|
||||||
// print(event, day, activeClassCounts[event][day]);
|
|
||||||
insertEventCount(event, day, activeClassCounts[event][day]);
|
log("Getting active class counts..");
|
||||||
}
|
var activeClassCounts = getActiveClassCounts(startDay);
|
||||||
|
// printjson(activeClassCounts);
|
||||||
|
// log("Inserting active class counts..");
|
||||||
|
for (var event in activeClassCounts) {
|
||||||
|
for (var day in activeClassCounts[event]) {
|
||||||
|
if (today === day) continue; // Never save data for today because it's incomplete
|
||||||
|
// print(event, day, activeClassCounts[event][day]);
|
||||||
|
insertEventCount(event, day, activeClassCounts[event][day]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log("Script runtime: " + (new Date() - scriptStartTime));
|
log("Script runtime: " + (new Date() - scriptStartTime));
|
||||||
}
|
|
||||||
catch(err) {
|
|
||||||
log("ERROR: " + err);
|
|
||||||
printjson(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getActiveClassCounts(startDay) {
|
function getActiveClassCounts(startDay) {
|
||||||
// Tally active classes per day, for paid, trial, and free
|
// Tally active classes per day, for paid, trial, and free
|
||||||
// TODO: does not handle class membership changes
|
|
||||||
|
|
||||||
if (!startDay) return {};
|
if (!startDay) return {};
|
||||||
|
|
||||||
|
@ -60,7 +58,7 @@ function getActiveClassCounts(startDay) {
|
||||||
// paid: at least one paid member
|
// paid: at least one paid member
|
||||||
// trial: not paid, at least one trial member
|
// trial: not paid, at least one trial member
|
||||||
// free: not paid, not free trial
|
// free: not paid, not free trial
|
||||||
// user.coursePrepaidID set means access to paid courses
|
// user.coursePrepaidID or user.coursePrepaid set means access to paid courses
|
||||||
// prepaid.properties.trialRequestID means access was via trial
|
// prepaid.properties.trialRequestID means access was via trial
|
||||||
|
|
||||||
// Find classroom users
|
// Find classroom users
|
||||||
|
@ -86,30 +84,46 @@ function getActiveClassCounts(startDay) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// log("DEBUG: Classroom users: " + classroomUserIDs.length);
|
||||||
|
|
||||||
log("Find user types..");
|
log("Find user types..");
|
||||||
var userEventMap = {};
|
var userEventEndDateMap = {};
|
||||||
var prepaidUsersMap = {};
|
var prepaidUsersMap = {};
|
||||||
var prepaidIDs = [];
|
var prepaidIDs = [];
|
||||||
cursor = db.users.find({_id: {$in: classroomUserObjectIds}}, {coursePrepaidID: 1});
|
cursor = db.users.find({_id: {$in: classroomUserObjectIds}}, {coursePrepaid: 1, coursePrepaidID: 1});
|
||||||
while (cursor.hasNext()) {
|
while (cursor.hasNext()) {
|
||||||
doc = cursor.next();
|
doc = cursor.next();
|
||||||
|
userEventEndDateMap[doc._id.valueOf()] = {};
|
||||||
|
userEventEndDateMap[doc._id.valueOf()][eventNameFree] = new Date();
|
||||||
|
if (doc.coursePrepaid) {
|
||||||
|
if (!doc.coursePrepaid.endDate) throw new Error("No endDate for new prepaid " + doc._id.valuOf());
|
||||||
|
userEventEndDateMap[doc._id.valueOf()][eventNamePaid] = new Date(doc.coursePrepaid.endDate);
|
||||||
|
if (!prepaidUsersMap[doc.coursePrepaid._id.valueOf()]) prepaidUsersMap[doc.coursePrepaid._id.valueOf()] = [];
|
||||||
|
prepaidUsersMap[doc.coursePrepaid._id.valueOf()].push(doc._id.valueOf());
|
||||||
|
prepaidIDs.push(doc.coursePrepaid._id);
|
||||||
|
}
|
||||||
if (doc.coursePrepaidID) {
|
if (doc.coursePrepaidID) {
|
||||||
userEventMap[doc._id.valueOf()] = eventNamePaid;
|
if (!userEventEndDateMap[doc._id.valueOf()][eventNamePaid]) {
|
||||||
|
userEventEndDateMap[doc._id.valueOf()][eventNamePaid] = new Date();
|
||||||
|
}
|
||||||
if (!prepaidUsersMap[doc.coursePrepaidID.valueOf()]) prepaidUsersMap[doc.coursePrepaidID.valueOf()] = [];
|
if (!prepaidUsersMap[doc.coursePrepaidID.valueOf()]) prepaidUsersMap[doc.coursePrepaidID.valueOf()] = [];
|
||||||
prepaidUsersMap[doc.coursePrepaidID.valueOf()].push(doc._id.valueOf());
|
prepaidUsersMap[doc.coursePrepaidID.valueOf()].push(doc._id.valueOf());
|
||||||
prepaidIDs.push(doc.coursePrepaidID);
|
prepaidIDs.push(doc.coursePrepaidID);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
userEventMap[doc._id.valueOf()] = eventNameFree;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
cursor = db.prepaids.find({_id: {$in: prepaidIDs}}, {properties: 1});
|
cursor = db.prepaids.find({_id: {$in: prepaidIDs}}, {endDate: 1, properties: 1});
|
||||||
while (cursor.hasNext()) {
|
while (cursor.hasNext()) {
|
||||||
doc = cursor.next();
|
doc = cursor.next();
|
||||||
if (doc.properties && doc.properties.trialRequestID) {
|
if (doc.properties && doc.properties.trialRequestID) {
|
||||||
|
var endDate = new Date();
|
||||||
|
if (doc.endDate) {
|
||||||
|
endDate = new Date(doc.endDate);
|
||||||
|
}
|
||||||
|
else if (doc.properties.endDate) {
|
||||||
|
endDate = new Date(doc.properties.endDate);
|
||||||
|
}
|
||||||
for (var i = 0; i < prepaidUsersMap[doc._id.valueOf()].length; i++) {
|
for (var i = 0; i < prepaidUsersMap[doc._id.valueOf()].length; i++) {
|
||||||
userEventMap[prepaidUsersMap[doc._id.valueOf()][i]] = eventNameTrial;
|
userEventEndDateMap[prepaidUsersMap[doc._id.valueOf()][i]][eventNameTrial] = endDate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,18 +135,22 @@ function getActiveClassCounts(startDay) {
|
||||||
var endDate = ISODate(startDay + "T00:00:00.000Z");
|
var endDate = ISODate(startDay + "T00:00:00.000Z");
|
||||||
var todayDate = new Date(new Date().toISOString().substring(0, 10));
|
var todayDate = new Date(new Date().toISOString().substring(0, 10));
|
||||||
var startObj = objectIdWithTimestamp(startDate);
|
var startObj = objectIdWithTimestamp(startDate);
|
||||||
var queryParams = {$and: [
|
// Batch size test times: 10k 427005, 5k 361361, 1k 799068, 2k 791521
|
||||||
{_id: {$gte: startObj}},
|
var batchSize = 5000;
|
||||||
{user: {$in: classroomUserIDs}},
|
for (var j = 0; j < classroomUserIDs.length / batchSize + 1; j++) {
|
||||||
{event: 'Started Level'}
|
// log("DEBUG: Fetching classroom events batch " + (j * batchSize) + " " + (j * batchSize + batchSize));
|
||||||
]};
|
var queryParams = {$and: [
|
||||||
cursor = logDB['log'].find(queryParams, {user: 1});
|
{_id: {$gte: startObj}},
|
||||||
while (cursor.hasNext()) {
|
{user: {$in: classroomUserIDs.slice(j * batchSize, j * batchSize + batchSize)}},
|
||||||
doc = cursor.next();
|
{event: 'Started Level'}
|
||||||
if (!userPlayedMap[doc.user]) userPlayedMap[doc.user] = [];
|
]};
|
||||||
userPlayedMap[doc.user].push(doc._id.getTimestamp());
|
cursor = analyticsDB['log'].find(queryParams, {user: 1});
|
||||||
|
while (cursor.hasNext()) {
|
||||||
|
doc = cursor.next();
|
||||||
|
if (!userPlayedMap[doc.user]) userPlayedMap[doc.user] = [];
|
||||||
|
userPlayedMap[doc.user].push(doc._id.getTimestamp());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// printjson(userPlayedMap);
|
|
||||||
|
|
||||||
log("Calculate number of active members per classroom per day per event type..");
|
log("Calculate number of active members per classroom per day per event type..");
|
||||||
var classDayTypeMap = {};
|
var classDayTypeMap = {};
|
||||||
|
@ -159,7 +177,19 @@ function getActiveClassCounts(startDay) {
|
||||||
if (userPlayedMap[member]) {
|
if (userPlayedMap[member]) {
|
||||||
for (var k = 0; k < userPlayedMap[member].length; k++) {
|
for (var k = 0; k < userPlayedMap[member].length; k++) {
|
||||||
if (userPlayedMap[member][k] > startDate && userPlayedMap[member][k] <= endDate) {
|
if (userPlayedMap[member][k] > startDate && userPlayedMap[member][k] <= endDate) {
|
||||||
classDayTypeMap[classroom][endDay][userEventMap[member]]++;
|
if (userEventEndDateMap[member][eventNameTrial] > endDate) {
|
||||||
|
classDayTypeMap[classroom][endDay][eventNameTrial]++;
|
||||||
|
}
|
||||||
|
else if (userEventEndDateMap[member][eventNamePaid] > endDate) {
|
||||||
|
classDayTypeMap[classroom][endDay][eventNamePaid]++;
|
||||||
|
}
|
||||||
|
else if (userEventEndDateMap[member][eventNameFree] > endDate) {
|
||||||
|
classDayTypeMap[classroom][endDay][eventNameFree]++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
print("ERROR: no event for " + member);
|
||||||
|
printjson(userEventEndDateMap[member]);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
// Usage:
|
// Usage:
|
||||||
// mongo <address>:<port>/<database> <script file> -u <username> -p <password>
|
// mongo <address>:<port>/<database> <script file> -u <username> -p <password>
|
||||||
|
|
||||||
|
// TODO: classroom paid active users before 4/13/16 not correct
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var logDB = new Mongo("localhost").getDB("analytics")
|
var logDB = new Mongo("localhost").getDB("analytics")
|
||||||
var scriptStartTime = new Date();
|
var scriptStartTime = new Date();
|
||||||
|
@ -129,32 +131,47 @@ function getActiveUserCounts(startDay, endDay, activeUserEvents) {
|
||||||
log("Classroom user count: " + classroomUserObjectIds.length);
|
log("Classroom user count: " + classroomUserObjectIds.length);
|
||||||
|
|
||||||
// Classrooms free/trial/paid
|
// Classrooms free/trial/paid
|
||||||
// Paid user: user.coursePrepaidID set means access to paid courses
|
// Paid user: user.coursePrepaid or user.coursePrepaidID set means access to paid courses
|
||||||
// Trial user: prepaid.properties.trialRequestID means access was via trial
|
// Trial user: prepaid.properties.trialRequestID means access was via trial
|
||||||
// Free: not paid, not trial
|
// Free: not paid, not trial
|
||||||
log("Finding classroom users free/trial/paid status..");
|
log("Finding classroom users free/trial/paid status..");
|
||||||
var classroomUserEventMap = {};
|
var classroomUserEventEndDateMap = {};
|
||||||
var prepaidUsersMap = {};
|
var prepaidUsersMap = {};
|
||||||
var prepaidIDs = [];
|
var prepaidIDs = [];
|
||||||
cursor = db.users.find({_id: {$in: classroomUserObjectIds}}, {coursePrepaidID: 1});
|
cursor = db.users.find({_id: {$in: classroomUserObjectIds}}, {coursePrepaid: 1, coursePrepaidID: 1});
|
||||||
while (cursor.hasNext()) {
|
while (cursor.hasNext()) {
|
||||||
doc = cursor.next();
|
doc = cursor.next();
|
||||||
|
classroomUserEventEndDateMap[doc._id.valueOf()] = {};
|
||||||
|
classroomUserEventEndDateMap[doc._id.valueOf()]['DAU classroom free'] = new Date();
|
||||||
|
if (doc.coursePrepaid) {
|
||||||
|
if (!doc.coursePrepaid.endDate) throw new Error("No endDate for new prepaid " + doc._id.valuOf());
|
||||||
|
classroomUserEventEndDateMap[doc._id.valueOf()]['DAU classroom paid'] = new Date(doc.coursePrepaid.endDate);
|
||||||
|
if (!prepaidUsersMap[doc.coursePrepaid._id.valueOf()]) prepaidUsersMap[doc.coursePrepaid._id.valueOf()] = [];
|
||||||
|
prepaidUsersMap[doc.coursePrepaid._id.valueOf()].push(doc._id.valueOf());
|
||||||
|
prepaidIDs.push(doc.coursePrepaid._id);
|
||||||
|
}
|
||||||
if (doc.coursePrepaidID) {
|
if (doc.coursePrepaidID) {
|
||||||
classroomUserEventMap[doc._id.valueOf()] = 'DAU classroom paid';
|
if (!classroomUserEventEndDateMap[doc._id.valueOf()]['DAU classroom paid']) {
|
||||||
|
classroomUserEventEndDateMap[doc._id.valueOf()]['DAU classroom paid'] = new Date();
|
||||||
|
}
|
||||||
if (!prepaidUsersMap[doc.coursePrepaidID.valueOf()]) prepaidUsersMap[doc.coursePrepaidID.valueOf()] = [];
|
if (!prepaidUsersMap[doc.coursePrepaidID.valueOf()]) prepaidUsersMap[doc.coursePrepaidID.valueOf()] = [];
|
||||||
prepaidUsersMap[doc.coursePrepaidID.valueOf()].push(doc._id.valueOf());
|
prepaidUsersMap[doc.coursePrepaidID.valueOf()].push(doc._id.valueOf());
|
||||||
prepaidIDs.push(doc.coursePrepaidID);
|
prepaidIDs.push(doc.coursePrepaidID);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
classroomUserEventMap[doc._id.valueOf()] = 'DAU classroom free';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
cursor = db.prepaids.find({_id: {$in: prepaidIDs}}, {properties: 1});
|
cursor = db.prepaids.find({_id: {$in: prepaidIDs}}, {properties: 1});
|
||||||
while (cursor.hasNext()) {
|
while (cursor.hasNext()) {
|
||||||
doc = cursor.next();
|
doc = cursor.next();
|
||||||
if (doc.properties && doc.properties.trialRequestID) {
|
if (doc.properties && doc.properties.trialRequestID) {
|
||||||
|
var endDate = new Date();
|
||||||
|
if (doc.endDate) {
|
||||||
|
endDate = new Date(doc.endDate);
|
||||||
|
}
|
||||||
|
else if (doc.properties.endDate) {
|
||||||
|
endDate = new Date(doc.properties.endDate);
|
||||||
|
}
|
||||||
for (var i = 0; i < prepaidUsersMap[doc._id.valueOf()].length; i++) {
|
for (var i = 0; i < prepaidUsersMap[doc._id.valueOf()].length; i++) {
|
||||||
classroomUserEventMap[prepaidUsersMap[doc._id.valueOf()][i]] = 'DAU classroom trial';
|
classroomUserEventEndDateMap[prepaidUsersMap[doc._id.valueOf()][i]]['DAU classroom trial'] = endDate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,7 +216,22 @@ function getActiveUserCounts(startDay, endDay, activeUserEvents) {
|
||||||
var userDayEventMap = {}
|
var userDayEventMap = {}
|
||||||
for (day in dayUserActiveMap) {
|
for (day in dayUserActiveMap) {
|
||||||
for (var user in dayUserActiveMap[day]) {
|
for (var user in dayUserActiveMap[day]) {
|
||||||
var event = classroomUserEventMap[user] || (dayCampaignUserPaidMap[day] && dayCampaignUserPaidMap[day][user] ? 'DAU campaign paid' : 'DAU campaign free');
|
var event = null;
|
||||||
|
var endDate = new Date(day + "T00:00:00.000Z");
|
||||||
|
if (classroomUserEventEndDateMap[user]) {
|
||||||
|
if (classroomUserEventEndDateMap[user]['DAU classroom trial'] > endDate) {
|
||||||
|
event = 'DAU classroom trial';
|
||||||
|
}
|
||||||
|
else if (classroomUserEventEndDateMap[user]['DAU classroom paid'] > endDate) {
|
||||||
|
event = 'DAU classroom paid';
|
||||||
|
}
|
||||||
|
else if (classroomUserEventEndDateMap[user]['DAU classroom free'] > endDate) {
|
||||||
|
event = 'DAU classroom free';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!event) {
|
||||||
|
event = dayCampaignUserPaidMap[day] && dayCampaignUserPaidMap[day][user] ? 'DAU campaign paid' : 'DAU campaign free';
|
||||||
|
}
|
||||||
dailyEventNames[event] = true;
|
dailyEventNames[event] = true;
|
||||||
if (!activeUsersCounts[day]) activeUsersCounts[day] = {};
|
if (!activeUsersCounts[day]) activeUsersCounts[day] = {};
|
||||||
if (!activeUsersCounts[day][event]) activeUsersCounts[day][event] = 0;
|
if (!activeUsersCounts[day][event]) activeUsersCounts[day][event] = 0;
|
||||||
|
|
Loading…
Reference in a new issue