mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-02-17 08:50:58 -05:00
Campaign analytics - unique users, dropped count
Fixing incorrect dropped counts. Restricting start/finish level events to unique users. Does not fix campaign editor level view analytics.
This commit is contained in:
parent
4ea2a51ff6
commit
d89b0d90e3
3 changed files with 119 additions and 22 deletions
82
scripts/analytics/mixpanelLevelRates.py
Normal file
82
scripts/analytics/mixpanelLevelRates.py
Normal file
|
@ -0,0 +1,82 @@
|
|||
# Calculate level completion rates via mixpanel export API
|
||||
|
||||
# TODO: unique users
|
||||
# TODO: align output
|
||||
# TODO: order output
|
||||
|
||||
import sys
|
||||
from mixpanel import Mixpanel
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
import simplejson as json
|
||||
|
||||
# NOTE: mixpanel dates are by day and inclusive
|
||||
# E.g. '2014-12-08' is any date that day, up to 2014-12-09 12am
|
||||
|
||||
if __name__ == '__main__':
|
||||
if not len(sys.argv) is 3:
|
||||
print "Script format: <script> <api_key> <api_secret>"
|
||||
else:
|
||||
api_key = sys.argv[1]
|
||||
api_secret = sys.argv[2]
|
||||
api = Mixpanel(
|
||||
api_key = api_key,
|
||||
api_secret = api_secret
|
||||
)
|
||||
|
||||
startDate = '2014-12-31'
|
||||
endDate = '2015-01-05'
|
||||
print("Requesting data for {0} to {1}".format(startDate, endDate))
|
||||
data = api.request(['export'], {
|
||||
'event' : ['Started Level', 'Saw Victory'],
|
||||
'from_date' : startDate,
|
||||
'to_date' : endDate
|
||||
})
|
||||
|
||||
levelRates = {}
|
||||
lines = data.split('\n')
|
||||
print "Received %d entries" % len(lines)
|
||||
for line in lines:
|
||||
try:
|
||||
if len(line) is 0: continue
|
||||
eventData = json.loads(line)
|
||||
eventName = eventData['event']
|
||||
if not eventName in ['Started Level', 'Saw Victory']:
|
||||
print 'Unexpected event ' + eventName
|
||||
break
|
||||
properties = eventData['properties']
|
||||
if 'levelID' in properties:
|
||||
levelID = properties['levelID']
|
||||
elif 'level' in properties:
|
||||
levelID = properties['level'].lower().replace(' ', '-')
|
||||
else:
|
||||
print("Unkonwn levelID for", eventName)
|
||||
print(properties)
|
||||
break
|
||||
if not levelID in levelRates:
|
||||
levelRates[levelID] = {'started': 0, 'finished': 0}
|
||||
if eventName == 'Started Level':
|
||||
levelRates[levelID]['started'] += 1
|
||||
elif eventName == 'Saw Victory':
|
||||
levelRates[levelID]['finished'] += 1
|
||||
else:
|
||||
print("Unknown event name", eventName)
|
||||
print(eventData)
|
||||
break
|
||||
except:
|
||||
print "Unexpected error:", sys.exc_info()[0]
|
||||
print line
|
||||
break
|
||||
|
||||
# print(levelRates)
|
||||
for levelID in levelRates:
|
||||
started = levelRates[levelID]['started']
|
||||
finished = levelRates[levelID]['finished']
|
||||
# if not levelID == 'endangered-burl':
|
||||
# continue
|
||||
if started > 0:
|
||||
print("{0}\t{1}\t{2}\t{3}%".format(levelID, started, finished, float(finished) / started * 100))
|
||||
else:
|
||||
print("{0}\t{1}\t{2}".format(levelID, started, finished))
|
|
@ -20,12 +20,15 @@ print("Today is " + today);
|
|||
var todayMinus6 = new Date();
|
||||
todayMinus6.setDate(todayMinus6.getUTCDate() - 6);
|
||||
var startDate = todayMinus6.toISOString().substr(0, 10) + "T00:00:00.000Z";
|
||||
// startDate = "2014-12-01T00:00:00.000Z";
|
||||
// startDate = "2014-12-31T00:00:00.000Z";
|
||||
print("Start date is " + startDate)
|
||||
// var endDate = "2015-01-06T00:00:00.000Z";
|
||||
// print("End date is " + endDate)
|
||||
|
||||
var cursor = db['analytics.log.events'].find({
|
||||
$and: [
|
||||
{"created": { $gte: ISODate(startDate)}},
|
||||
// {"created": { $lt: ISODate(endDate)}},
|
||||
{$or: [ {"event" : 'Started Level'}, {"event" : 'Saw Victory'}]}
|
||||
]
|
||||
});
|
||||
|
@ -115,6 +118,7 @@ var campaigns = {
|
|||
// Bucketize events by user
|
||||
print("Getting event data...");
|
||||
var userProgression = {};
|
||||
var userLevelEventMap = {}; // Only want unique users per-level/event
|
||||
while (cursor.hasNext()) {
|
||||
var doc = cursor.next();
|
||||
var created = doc.created;
|
||||
|
@ -124,12 +128,17 @@ while (cursor.hasNext()) {
|
|||
if (level) {
|
||||
if (level.length > longestLevelName) longestLevelName = level.length;
|
||||
var user = doc.user.valueOf();
|
||||
if (!userProgression[user]) userProgression[user] = [];
|
||||
userProgression[user].push({
|
||||
created: created,
|
||||
event: event,
|
||||
level: level
|
||||
});
|
||||
if (!userLevelEventMap[user]) userLevelEventMap[user] = {};
|
||||
if (!userLevelEventMap[user][level]) userLevelEventMap[user][level] = {};
|
||||
if (!userLevelEventMap[user][level][event]) {
|
||||
userLevelEventMap[user][level][event] = true;
|
||||
if (!userProgression[user]) userProgression[user] = [];
|
||||
userProgression[user].push({
|
||||
created: created,
|
||||
event: event,
|
||||
level: level
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
longestLevelName += 2;
|
||||
|
@ -224,7 +233,7 @@ for (campaign in campaigns) {
|
|||
print("\n" + campaign);
|
||||
var level = "level";
|
||||
var levelSpacer = new Array(longestLevelName - level.length).join(' ');
|
||||
print(level + levelSpacer + "started\tdropped\t\tfinished dropped");
|
||||
print(level + levelSpacer + "started\tdropped\t\tfinished dropped\tcompletion");
|
||||
for (var i = 0; i < campaignRates[campaign].levels.length; i++) {
|
||||
var level = campaignRates[campaign].levels[i].level;
|
||||
var started = campaignRates[campaign].levels[i].started;
|
||||
|
@ -232,13 +241,13 @@ for (campaign in campaigns) {
|
|||
var finished = campaignRates[campaign].levels[i].finished;
|
||||
var finishDropped = campaignRates[campaign].levels[i].finishDropped;
|
||||
var levelSpacer = new Array(longestLevelName - level.length).join(' ');
|
||||
print(level + levelSpacer + started + "\t" + (started < 100 ? "\t" : "") + startDropped + "\t" + (startDropped / started * 100).toFixed(2) + "%\t" + finished + "\t" + finishDropped + "\t" + (finishDropped / finished * 100).toFixed(2) + "%");
|
||||
print(level + levelSpacer + started + "\t" + (started < 100 ? "\t" : "") + startDropped + "\t" + (startDropped / started * 100).toFixed(2) + "%\t" + finished + "\t" + finishDropped + "\t" + (finishDropped / finished * 100).toFixed(2) + "%" + "\t" + (finished / started * 100).toFixed(2) + "%");
|
||||
}
|
||||
var level = 'Overall';
|
||||
var started = campaignRates[campaign].overall.started;
|
||||
var startDropped = campaignRates[campaign].overall.startDropped;
|
||||
var finished = campaignRates[campaign].overall.finished;
|
||||
var finishDropped = campaignRates[campaign].overall.finishDropped;
|
||||
var levelSpacer = new Array(longestLevelName - level.length).join(' ');
|
||||
print(level + levelSpacer + started + "\t" + (started < 100 ? "\t" : "") + startDropped + "\t" + (startDropped / started * 100).toFixed(2) + "%\t" + finished + "\t" + finishDropped + "\t" + (finishDropped / finished * 100).toFixed(2) + "%");
|
||||
// var level = 'Overall';
|
||||
// var started = campaignRates[campaign].overall.started;
|
||||
// var startDropped = campaignRates[campaign].overall.startDropped;
|
||||
// var finished = campaignRates[campaign].overall.finished;
|
||||
// var finishDropped = campaignRates[campaign].overall.finishDropped;
|
||||
// var levelSpacer = new Array(longestLevelName - level.length).join(' ');
|
||||
// print(level + levelSpacer + started + "\t" + (started < 100 ? "\t" : "") + startDropped + "\t" + (startDropped / started * 100).toFixed(2) + "%\t" + finished + "\t" + finishDropped + "\t" + (finishDropped / finished * 100).toFixed(2) + "%");
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ class AnalyticsLogEventHandler extends Handler
|
|||
# endDay - Exclusive, optional, e.g. '2014-12-16'
|
||||
|
||||
# TODO: An uncached call takes about 15s locally
|
||||
# TODO: Use unique users
|
||||
|
||||
levelSlug = req.query.slug or req.body.slug
|
||||
startDay = req.query.startDay or req.body.startDay
|
||||
|
@ -142,6 +143,7 @@ class AnalyticsLogEventHandler extends Handler
|
|||
|
||||
# Bucketize events by user
|
||||
userProgression = {}
|
||||
userLevelEventMap = {} # Only want unique users per-level/event
|
||||
for item in data
|
||||
created = item.get('created')
|
||||
event = item.get('event')
|
||||
|
@ -151,14 +153,18 @@ class AnalyticsLogEventHandler extends Handler
|
|||
level = item.get('properties.levelID')
|
||||
continue unless level?
|
||||
user = item.get('user')
|
||||
userProgression[user] ?= []
|
||||
userProgression[user].push
|
||||
created: created
|
||||
event: event
|
||||
level: level
|
||||
userLevelEventMap[user] ?= {}
|
||||
userLevelEventMap[user][level] ?= {}
|
||||
unless userLevelEventMap[user][level][event]
|
||||
userLevelEventMap[user][level][event] = true
|
||||
userProgression[user] ?= []
|
||||
userProgression[user].push
|
||||
created: created
|
||||
event: event
|
||||
level: level
|
||||
|
||||
# Order user progression by created
|
||||
for user in userProgression
|
||||
for user of userProgression
|
||||
userProgression[user].sort (a,b) -> if a.created < b.created then return -1 else 1
|
||||
|
||||
# Per-level start/drop/finish/drop
|
||||
|
|
Loading…
Reference in a new issue