mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-04-25 05:23:39 -04:00
Update campaign editor analytics
Adding ‘left game’ counts to overall campaign view. This is the number of players that left the game after playing the given level.
This commit is contained in:
parent
513772d70e
commit
05f028d944
7 changed files with 257 additions and 70 deletions
app
styles/editor/campaign
templates/editor/campaign
views/editor/campaign
scripts/analytics/mongodb/queries
server
3
app/styles/editor/campaign/campaign-analytics-modal.sass
Normal file
3
app/styles/editor/campaign/campaign-analytics-modal.sass
Normal file
|
@ -0,0 +1,3 @@
|
|||
#campaign-analytics-modal
|
||||
.modal-dialog
|
||||
width: 75%
|
|
@ -21,13 +21,3 @@
|
|||
bottom: 0
|
||||
right: 0
|
||||
width: 75%
|
||||
|
||||
#analytics-button
|
||||
position: absolute
|
||||
right: 1%
|
||||
top: 1%
|
||||
padding: 3px 8px
|
||||
|
||||
#analytics-modal
|
||||
.modal-content
|
||||
background-color: white
|
||||
|
|
|
@ -16,7 +16,10 @@ block modal-body-content
|
|||
td Level
|
||||
td Started
|
||||
td Finished
|
||||
td Left Game
|
||||
td LG %
|
||||
td Playtime (s)
|
||||
td LG/s
|
||||
td Completion %
|
||||
tbody
|
||||
- for (var i = 0; i < campaignCompletions.levels.length; i++)
|
||||
|
@ -24,10 +27,19 @@ block modal-body-content
|
|||
td= campaignCompletions.levels[i].level
|
||||
td= campaignCompletions.levels[i].started
|
||||
td= campaignCompletions.levels[i].finished
|
||||
td= campaignCompletions.levels[i].dropped
|
||||
if campaignCompletions.top3DropPercentage && campaignCompletions.top3DropPercentage.indexOf(campaignCompletions.levels[i].level) >= 0
|
||||
td(style='background-color:pink;')= td= campaignCompletions.levels[i].dropPercentage
|
||||
else
|
||||
td= campaignCompletions.levels[i].dropPercentage
|
||||
td= campaignCompletions.levels[i].averagePlaytime
|
||||
if campaignCompletions.top3.indexOf(campaignCompletions.levels[i].level) >= 0
|
||||
if campaignCompletions.top3DropPerSecond && campaignCompletions.top3DropPerSecond.indexOf(campaignCompletions.levels[i].level) >= 0
|
||||
td(style='background-color:pink;')= td= campaignCompletions.levels[i].droppedPerSecond
|
||||
else
|
||||
td= campaignCompletions.levels[i].droppedPerSecond
|
||||
if campaignCompletions.top3 && campaignCompletions.top3.indexOf(campaignCompletions.levels[i].level) >= 0
|
||||
td(style='background-color:lightblue;')= campaignCompletions.levels[i].completionRate
|
||||
else if campaignCompletions.bottom3.indexOf(campaignCompletions.levels[i].level) >= 0
|
||||
else if campaignCompletions.bottom3 && campaignCompletions.bottom3.indexOf(campaignCompletions.levels[i].level) >= 0
|
||||
td(style='background-color:pink;')= campaignCompletions.levels[i].completionRate
|
||||
else
|
||||
td= campaignCompletions.levels[i].completionRate
|
||||
|
|
|
@ -31,47 +31,41 @@ module.exports = class CampaignAnalyticsModal extends ModalView
|
|||
startDay = $('#input-startday').val()
|
||||
endDay = $('#input-endday').val()
|
||||
delete @campaignCompletions.levels
|
||||
@campaignCompletions.startDay = startDay
|
||||
@campaignCompletions.endDay = endDay
|
||||
@render()
|
||||
@getCampaignAnalytics startDay, endDay
|
||||
|
||||
getCampaignAnalytics: (startDay, endDay) =>
|
||||
# Fetch campaign analytics, unless dates given
|
||||
if startDay?
|
||||
startDayDashed = startDay
|
||||
startDay = startDay.replace(/-/g, '')
|
||||
else
|
||||
startDay = utils.getUTCDay -14
|
||||
startDayDashed = "#{startDay[0..3]}-#{startDay[4..5]}-#{startDay[6..7]}"
|
||||
if endDay?
|
||||
endDayDashed = endDay
|
||||
endDay = endDay.replace(/-/g, '')
|
||||
else
|
||||
endDay = utils.getUTCDay -1
|
||||
endDayDashed = "#{endDay[0..3]}-#{endDay[4..5]}-#{endDay[6..7]}"
|
||||
@campaignCompletions.startDay = startDayDashed
|
||||
@campaignCompletions.endDay = endDayDashed
|
||||
|
||||
startDay = startDay.replace(/-/g, '') if startDay?
|
||||
endDay = endDay.replace(/-/g, '') if endDay?
|
||||
# Chain these together so we can calculate relative metrics (e.g. left game per second)
|
||||
@getCampaignLevelCompletions startDay, endDay, () =>
|
||||
@render()
|
||||
@getCompaignLevelDrops startDay, endDay, () =>
|
||||
@render()
|
||||
@getCampaignAveragePlaytimes startDayDashed, endDayDashed, () =>
|
||||
@render()
|
||||
|
||||
startDay ?= utils.getUTCDay -14
|
||||
endDay ?= utils.getUTCDay -1
|
||||
|
||||
success = (data) =>
|
||||
return if @destroyed
|
||||
mapFn = (item) ->
|
||||
item.completionRate = (item.finished / item.started * 100).toFixed(2)
|
||||
item
|
||||
@campaignCompletions.levels = _.map data, mapFn, @
|
||||
sortedLevels = _.cloneDeep @campaignCompletions.levels
|
||||
sortedLevels = _.filter sortedLevels, ((a) -> a.finished >= 10), @
|
||||
sortedLevels.sort (a, b) -> b.completionRate - a.completionRate
|
||||
@campaignCompletions.top3 = _.pluck sortedLevels[0..2], 'level'
|
||||
sortedLevels.sort (a, b) -> a.completionRate - b.completionRate
|
||||
@campaignCompletions.bottom3 = _.pluck sortedLevels[0..2], 'level'
|
||||
@campaignCompletions.startDay = "#{startDay[0..3]}-#{startDay[4..5]}-#{startDay[6..7]}"
|
||||
@campaignCompletions.endDay = "#{endDay[0..3]}-#{endDay[4..5]}-#{endDay[6..7]}"
|
||||
@getCampaignAveragePlaytimes startDay, endDay
|
||||
|
||||
# TODO: Why do we need this url dash?
|
||||
request = @supermodel.addRequestResource 'campaign_completions', {
|
||||
url: '/db/analytics_perday/-/campaign_completions'
|
||||
data: {startDay: startDay, endDay: endDay, slug: @campaignHandle}
|
||||
method: 'POST'
|
||||
success: success
|
||||
}, 0
|
||||
request.load()
|
||||
|
||||
getCampaignAveragePlaytimes: (startDay, endDay) =>
|
||||
getCampaignAveragePlaytimes: (startDay, endDay, doneCallback) =>
|
||||
# Fetch level average playtimes
|
||||
# Needs date format yyyy-mm-dd
|
||||
success = (data) =>
|
||||
return if @destroyed
|
||||
# console.log 'getCampaignAveragePlaytimes success', data
|
||||
levelAverages = {}
|
||||
for item in data
|
||||
levelAverages[item.level] ?= []
|
||||
|
@ -81,14 +75,16 @@ module.exports = class CampaignAnalyticsModal extends ModalView
|
|||
if levelAverages[level.level].length > 0
|
||||
total = _.reduce levelAverages[level.level], ((sum, num) -> sum + num)
|
||||
level.averagePlaytime = (total / levelAverages[level.level].length).toFixed(2)
|
||||
if level.averagePlaytime > 0 and level.dropped > 0
|
||||
level.droppedPerSecond = (level.dropped / level.averagePlaytime).toFixed(2)
|
||||
else
|
||||
level.averagePlaytime = 0.0
|
||||
@render()
|
||||
|
||||
startDay ?= utils.getUTCDay -14
|
||||
startDay = "#{startDay[0..3]}-#{startDay[4..5]}-#{startDay[6..7]}"
|
||||
endDay ?= utils.getUTCDay -1
|
||||
endDay = "#{endDay[0..3]}-#{endDay[4..5]}-#{endDay[6..7]}"
|
||||
|
||||
sortedLevels = _.cloneDeep @campaignCompletions.levels
|
||||
sortedLevels = _.filter sortedLevels, ((a) -> a.droppedPerSecond > 0), @
|
||||
sortedLevels.sort (a, b) -> b.droppedPerSecond - a.droppedPerSecond
|
||||
@campaignCompletions.top3DropPerSecond = _.pluck sortedLevels[0..2], 'level'
|
||||
doneCallback()
|
||||
|
||||
levelSlugs = _.pluck @campaignCompletions.levels, 'level'
|
||||
|
||||
|
@ -99,3 +95,62 @@ module.exports = class CampaignAnalyticsModal extends ModalView
|
|||
success: success
|
||||
}, 0
|
||||
request.load()
|
||||
|
||||
getCampaignLevelCompletions: (startDay, endDay, doneCallback) =>
|
||||
# Needs date format yyyymmdd
|
||||
success = (data) =>
|
||||
return if @destroyed
|
||||
# console.log 'getCampaignLevelCompletions success', data
|
||||
mapFn = (item) ->
|
||||
item.completionRate = if item.started > 0 then (item.finished / item.started * 100).toFixed(2) else 0.0
|
||||
item
|
||||
@campaignCompletions.levels = _.map data, mapFn, @
|
||||
|
||||
sortedLevels = _.cloneDeep @campaignCompletions.levels
|
||||
sortedLevels = _.filter sortedLevels, ((a) -> a.finished >= 10), @
|
||||
if sortedLevels.length >= 3
|
||||
sortedLevels.sort (a, b) -> b.completionRate - a.completionRate
|
||||
@campaignCompletions.top3 = _.pluck sortedLevels[0..2], 'level'
|
||||
@campaignCompletions.bottom3 = _.pluck sortedLevels[sortedLevels.length - 4...sortedLevels.length - 1], 'level'
|
||||
|
||||
doneCallback()
|
||||
|
||||
# TODO: Why do we need this url dash?
|
||||
request = @supermodel.addRequestResource 'campaign_completions', {
|
||||
url: '/db/analytics_perday/-/campaign_completions'
|
||||
data: {startDay: startDay, endDay: endDay, slug: @campaignHandle}
|
||||
method: 'POST'
|
||||
success: success
|
||||
}, 0
|
||||
request.load()
|
||||
|
||||
getCompaignLevelDrops: (startDay, endDay, doneCallback) =>
|
||||
# Fetch level drops
|
||||
# Needs date format yyyymmdd
|
||||
success = (data) =>
|
||||
return if @destroyed
|
||||
# console.log 'getCompaignLevelDrops success', data
|
||||
levelDrops = {}
|
||||
for item in data
|
||||
levelDrops[item.level] ?= item.dropped
|
||||
for level in @campaignCompletions.levels
|
||||
level.dropped = levelDrops[level.level] ? 0
|
||||
level.dropPercentage = (level.dropped / level.started * 100).toFixed(2) if level.started > 0
|
||||
|
||||
sortedLevels = _.cloneDeep @campaignCompletions.levels
|
||||
sortedLevels = _.filter sortedLevels, ((a) -> a.dropPercentage > 0), @
|
||||
if sortedLevels.length >= 3
|
||||
sortedLevels.sort (a, b) -> b.dropPercentage - a.dropPercentage
|
||||
@campaignCompletions.top3DropPercentage = _.pluck sortedLevels[0..2], 'level'
|
||||
doneCallback()
|
||||
|
||||
return unless @campaignCompletions?.levels?
|
||||
levelSlugs = _.pluck @campaignCompletions.levels, 'level'
|
||||
|
||||
request = @supermodel.addRequestResource 'level_drops', {
|
||||
url: '/db/analytics_perday/-/level_drops'
|
||||
data: {startDay: startDay, endDay: endDay, slugs: levelSlugs}
|
||||
method: 'POST'
|
||||
success: success
|
||||
}, 0
|
||||
request.load()
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
// Finish count for the same start date is how many unique users finished the remaining steps in the following ~30 days
|
||||
// https://mixpanel.com/help/questions/articles/how-are-funnels-calculated
|
||||
|
||||
// Drop count: last started or finished level event for a given unique user
|
||||
|
||||
// TODO: Why are Mixpanel level finish events significantly lower?
|
||||
// TODO: dungeons-of-kithgard completion rate is 62% vs. 77%
|
||||
// TODO: Similar start events, finish events off by 20% (5334 vs 6486)
|
||||
|
@ -74,11 +76,12 @@ function getLevelFunnelData(startDay, eventFunnel) {
|
|||
var queryParams = {$and: [{_id: {$gte: startObj}},{"event": {$in: eventFunnel}}]};
|
||||
var cursor = db['analytics.log.events'].find(queryParams);
|
||||
|
||||
// Map ordering: level, user, event, created
|
||||
// Map ordering: level, user, event, day
|
||||
var userDataMap = {};
|
||||
while (cursor.hasNext()) {
|
||||
var doc = cursor.next();
|
||||
var created = doc._id.getTimestamp().toISOString().substring(0, 10);
|
||||
var created = doc._id.getTimestamp().toISOString();
|
||||
var day = created.substring(0, 10);
|
||||
var event = doc.event;
|
||||
var properties = doc.properties;
|
||||
var user = doc.user;
|
||||
|
@ -91,13 +94,13 @@ function getLevelFunnelData(startDay, eventFunnel) {
|
|||
|
||||
if (!userDataMap[level]) userDataMap[level] = {};
|
||||
if (!userDataMap[level][user]) userDataMap[level][user] = {};
|
||||
if (!userDataMap[level][user][event] || userDataMap[level][user][event].localeCompare(created) > 0) {
|
||||
// if (userDataMap[level][user][event]) log("Found earlier date " + level + " " + event + " " + user + " " + userDataMap[level][user][event] + " " + created);
|
||||
userDataMap[level][user][event] = created;
|
||||
if (!userDataMap[level][user][event] || userDataMap[level][user][event].localeCompare(day) > 0) {
|
||||
// if (userDataMap[level][user][event]) log("Found earlier date " + level + " " + event + " " + user + " " + userDataMap[level][user][event] + " " + day);
|
||||
userDataMap[level][user][event] = day;
|
||||
}
|
||||
}
|
||||
|
||||
// Data: level, created, event
|
||||
// Data: level, day, event
|
||||
var levelFunnelData = {};
|
||||
for (level in userDataMap) {
|
||||
for (user in userDataMap[level]) {
|
||||
|
@ -105,14 +108,14 @@ function getLevelFunnelData(startDay, eventFunnel) {
|
|||
// Find first event date
|
||||
var funnelStartDay = null;
|
||||
for (event in userDataMap[level][user]) {
|
||||
var created = userDataMap[level][user][event];
|
||||
var day = userDataMap[level][user][event];
|
||||
if (!levelFunnelData[level]) levelFunnelData[level] = {};
|
||||
if (!levelFunnelData[level][created]) levelFunnelData[level][created] = {};
|
||||
if (!levelFunnelData[level][created][event]) levelFunnelData[level][created][event] = 0;
|
||||
if (!levelFunnelData[level][day]) levelFunnelData[level][day] = {};
|
||||
if (!levelFunnelData[level][day][event]) levelFunnelData[level][day][event] = 0;
|
||||
if (eventFunnel[0] === event) {
|
||||
// First event gets attributed to current date
|
||||
levelFunnelData[level][created][event]++;
|
||||
funnelStartDay = created;
|
||||
levelFunnelData[level][day][event]++;
|
||||
funnelStartDay = day;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -135,6 +138,51 @@ function getLevelFunnelData(startDay, eventFunnel) {
|
|||
return levelFunnelData;
|
||||
}
|
||||
|
||||
function getLevelDropCounts(startDay, events) {
|
||||
// How many unique users did one of these events last?
|
||||
// Return level/day breakdown
|
||||
|
||||
if (!startDay || !events || events.length === 0) return {};
|
||||
|
||||
var startObj = objectIdWithTimestamp(ISODate(startDay + "T00:00:00.000Z"));
|
||||
var queryParams = {$and: [{_id: {$gte: startObj}},{"event": {$in: events}}]};
|
||||
var cursor = db['analytics.log.events'].find(queryParams);
|
||||
|
||||
var userProgression = {};
|
||||
while (cursor.hasNext()) {
|
||||
var doc = cursor.next();
|
||||
var created = doc._id.getTimestamp().toISOString();
|
||||
var event = doc.event;
|
||||
var properties = doc.properties;
|
||||
var user = doc.user;
|
||||
var level;
|
||||
|
||||
// TODO: Switch to properties.levelID for 'Saw Victory'
|
||||
if (event === 'Saw Victory' && properties.level) level = properties.level.toLowerCase().replace(/ /g, '-');
|
||||
else if (properties.levelID) level = properties.levelID
|
||||
else continue
|
||||
|
||||
if (!userProgression[user]) userProgression[user] = [];
|
||||
userProgression[user].push({
|
||||
created: created,
|
||||
event: event,
|
||||
level: level
|
||||
});
|
||||
}
|
||||
|
||||
var levelDropCounts = {};
|
||||
for (user in userProgression) {
|
||||
userProgression[user].sort(function (a,b) {return a.created < b.created ? -1 : 1});
|
||||
var lastEvent = userProgression[user][userProgression[user].length - 1];
|
||||
var level = lastEvent.level;
|
||||
var day = lastEvent.created.substring(0, 10);
|
||||
if (!levelDropCounts[level]) levelDropCounts[level] = {};
|
||||
if (!levelDropCounts[level][day]) levelDropCounts[level][day] = 0
|
||||
levelDropCounts[level][day]++;
|
||||
}
|
||||
return levelDropCounts;
|
||||
}
|
||||
|
||||
function insertEventCount(event, level, day, count) {
|
||||
// analytics.perdays schema in server/analytics/AnalyticsPeryDay.coffee
|
||||
day = day.replace(/-/g, '');
|
||||
|
@ -153,7 +201,7 @@ function insertEventCount(event, level, day, count) {
|
|||
// log("Updating count in db for " + day + " " + event + " " + level + " " + doc.c + " => " + count);
|
||||
var results = db['analytics.perdays'].update(queryParams, {$set: {c: count}});
|
||||
if (results.nMatched !== 1 && results.nModified !== 1) {
|
||||
log("ERROR: update count failed");
|
||||
log("ERROR: update event count failed");
|
||||
printjson(results);
|
||||
}
|
||||
}
|
||||
|
@ -191,13 +239,25 @@ try {
|
|||
|
||||
log("Inserting aggregated level completion data...");
|
||||
for (level in levelCompletionData) {
|
||||
for (created in levelCompletionData[level]) {
|
||||
if (today === created) continue; // Never save data for today because it's incomplete
|
||||
for (event in levelCompletionData[level][created]) {
|
||||
insertEventCount(event, level, created, levelCompletionData[level][created][event]);
|
||||
for (day in levelCompletionData[level]) {
|
||||
if (today === day) continue; // Never save data for today because it's incomplete
|
||||
for (event in levelCompletionData[level][day]) {
|
||||
insertEventCount(event, level, day, levelCompletionData[level][day][event]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log("Getting level drop counts...");
|
||||
var levelDropCounts = getLevelDropCounts(startDay, levelCompletionFunnel)
|
||||
|
||||
log("Inserting level drop counts...");
|
||||
var levelDropCounts = getLevelDropCounts(startDay, levelCompletionFunnel)
|
||||
for (level in levelDropCounts) {
|
||||
for (day in levelDropCounts[level]) {
|
||||
if (today === day) continue; // Never save data for today because it's incomplete
|
||||
insertEventCount('User Dropped', level, day, levelDropCounts[level][day]);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(err) {
|
||||
log("ERROR: " + err);
|
||||
|
|
|
@ -15,10 +15,11 @@ class AnalyticsPerDayHandler extends Handler
|
|||
getByRelationship: (req, res, args...) ->
|
||||
return @getCampaignCompletionsBySlug(req, res) if args[1] is 'campaign_completions'
|
||||
return @getLevelCompletionsBySlug(req, res) if args[1] is 'level_completions'
|
||||
return @getLevelDropsBySlugs(req, res) if args[1] is 'level_drops'
|
||||
super(arguments...)
|
||||
|
||||
getCampaignCompletionsBySlug: (req, res) ->
|
||||
# Send back an array of level starts and finishes
|
||||
# Send back an ordered array of level starts and finishes
|
||||
# Parameters:
|
||||
# slug - campaign slug
|
||||
# startDay - Inclusive, optional, YYYYMMDD e.g. '20141214'
|
||||
|
@ -41,7 +42,7 @@ class AnalyticsPerDayHandler extends Handler
|
|||
cacheKey = campaignSlug
|
||||
cacheKey += 's' + startDay if startDay?
|
||||
cacheKey += 'e' + endDay if endDay?
|
||||
return @sendSuccess res, campaignDropOffs if campaignDropOffs = @campaignCompletionsCache[cacheKey]
|
||||
return @sendSuccess res, completions if completions = @campaignCompletionsCache[cacheKey]
|
||||
|
||||
getCompletions = (orderedLevelSlugs, levelStringIDSlugMap) =>
|
||||
# 3. Send back an array of level starts and finishes
|
||||
|
@ -80,8 +81,8 @@ class AnalyticsPerDayHandler extends Handler
|
|||
for levelID of levelEventCounts
|
||||
completions.push
|
||||
level: levelStringIDSlugMap[levelID]
|
||||
started: levelEventCounts[levelID][startEventID]
|
||||
finished: levelEventCounts[levelID][finishEventID]
|
||||
started: levelEventCounts[levelID][startEventID] ? 0
|
||||
finished: levelEventCounts[levelID][finishEventID] ? 0
|
||||
completions.sort (a, b) -> orderedLevelSlugs.indexOf(a.level) - orderedLevelSlugs.indexOf(b.level)
|
||||
|
||||
@campaignCompletionsCache[cacheKey] = completions
|
||||
|
@ -124,6 +125,69 @@ class AnalyticsPerDayHandler extends Handler
|
|||
campaignLevels.push level for level of doc.get('levels') for doc in documents
|
||||
getLevelData campaignLevels
|
||||
|
||||
getLevelDropsBySlugs: (req, res) ->
|
||||
# Send back an array of level/drops
|
||||
# Drops - Number of unique users for which this was the last level they played
|
||||
# Parameters:
|
||||
# slugs - level slugs
|
||||
# startDay - Inclusive, optional, YYYYMMDD e.g. '20141214'
|
||||
# endDay - Exclusive, optional, YYYYMMDD e.g. '20141216'
|
||||
|
||||
levelSlugs = req.query.slugs or req.body.slugs
|
||||
startDay = req.query.startDay or req.body.startDay
|
||||
endDay = req.query.endDay or req.body.endDay
|
||||
|
||||
# log.warn "level_drops levelSlugs='#{levelSlugs}' startDay=#{startDay} endDay=#{endDay}"
|
||||
|
||||
return @sendSuccess res, [] unless levelSlugs?
|
||||
|
||||
# Cache results in app server memory for 1 day
|
||||
@levelDropsCache ?= {}
|
||||
@levelDropsCachedSince ?= new Date()
|
||||
if (new Date()) - @levelDropsCachedSince > 86400 * 1000
|
||||
@levelDropsCache = {}
|
||||
@levelDropsCachedSince = new Date()
|
||||
cacheKey = levelSlugs.join ''
|
||||
cacheKey += 's' + startDay if startDay?
|
||||
cacheKey += 'e' + endDay if endDay?
|
||||
return @sendSuccess res, drops if drops = @levelDropsCache[cacheKey]
|
||||
|
||||
AnalyticsString.find({v: {$in: ['User Dropped', 'all'].concat(levelSlugs)}}).exec (err, documents) =>
|
||||
if err? then return @sendDatabaseError res, err
|
||||
|
||||
levelStringIDSlugMap = {}
|
||||
for doc in documents
|
||||
droppedEventID = doc._id if doc.v is 'User Dropped'
|
||||
filterEventID = doc._id if doc.v is 'all'
|
||||
levelStringIDSlugMap[doc._id] = doc.v if doc.v in levelSlugs
|
||||
|
||||
return @sendSuccess res, [] unless droppedEventID? and filterEventID?
|
||||
|
||||
queryParams = {$and: [
|
||||
{e: droppedEventID},
|
||||
{f: filterEventID},
|
||||
{l: {$in: Object.keys(levelStringIDSlugMap)}}
|
||||
]}
|
||||
queryParams["$and"].push {d: {$gte: startDay}} if startDay?
|
||||
queryParams["$and"].push {d: {$lt: endDay}} if endDay?
|
||||
AnalyticsPerDay.find(queryParams).exec (err, documents) =>
|
||||
if err? then return @sendDatabaseError res, err
|
||||
|
||||
levelEventCounts = {}
|
||||
for doc in documents
|
||||
levelEventCounts[doc.l] ?= {}
|
||||
levelEventCounts[doc.l][doc.e] ?= 0
|
||||
levelEventCounts[doc.l][doc.e] += doc.c
|
||||
|
||||
drops = []
|
||||
for levelID of levelEventCounts
|
||||
drops.push
|
||||
level: levelStringIDSlugMap[levelID]
|
||||
dropped: levelEventCounts[levelID][droppedEventID] ? 0
|
||||
|
||||
@levelDropsCache[cacheKey] = drops
|
||||
@sendSuccess res, drops
|
||||
|
||||
getLevelCompletionsBySlug: (req, res) ->
|
||||
# Returns an array of per-day starts and finishes for given level
|
||||
# Parameters:
|
||||
|
|
|
@ -7,6 +7,7 @@ Handler = require '../commons/Handler'
|
|||
mongoose = require 'mongoose'
|
||||
async = require 'async'
|
||||
utils = require '../lib/utils'
|
||||
log = require 'winston'
|
||||
|
||||
LevelHandler = class LevelHandler extends Handler
|
||||
modelClass: Level
|
||||
|
@ -363,6 +364,8 @@ LevelHandler = class LevelHandler extends Handler
|
|||
|
||||
return @sendSuccess res, [] unless levelSlugs?
|
||||
|
||||
# log.warn "playtime_averages levelSlugs='#{levelSlugs}' startDay=#{startDay} endDay=#{endDay}"
|
||||
|
||||
# Cache results for 1 day
|
||||
@levelPlaytimesCache ?= {}
|
||||
@levelPlaytimesCachedSince ?= new Date()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue