Add admin analytics page with MAUs

Includes updating analytics insert script used to inject aggregated
data into production database.
This commit is contained in:
Matt Lott 2015-11-04 10:54:40 -08:00
parent d987b644a9
commit d445024cb6
6 changed files with 192 additions and 7 deletions
scripts/analytics/mongodb/queries

View file

@ -17,8 +17,8 @@ try {
var scriptStartTime = new Date();
var analyticsStringCache = {};
// Look at last 30 days, same as Mixpanel
var numDays = 30;
var numDays = 32;
var daysInMonth = 30;
var startDay = new Date();
today = startDay.toISOString().substr(0, 10);
@ -27,6 +27,7 @@ try {
var levelCompletionFunnel = ['Started Level', 'Saw Victory'];
var levelHelpEvents = ['Problem alert help clicked', 'Spell palette help clicked', 'Start help video'];
var activeUserEvents = ['Finished Signup', 'Started Level'];
log("Today is " + today);
log("Start day is " + startDay);
@ -39,7 +40,7 @@ try {
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]);
insertLevelEventCount(event, level, day, levelCompletionData[level][day][event]);
}
}
}
@ -50,7 +51,7 @@ try {
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]);
insertLevelEventCount('User Dropped', level, day, levelDropCounts[level][day]);
}
}
@ -61,7 +62,7 @@ try {
for (day in levelHelpCounts[level]) {
if (today === day) continue; // Never save data for today because it's incomplete
for (event in levelHelpCounts[level][day]) {
insertEventCount(event, level, day, levelHelpCounts[level][day][event]);
insertLevelEventCount(event, level, day, levelHelpCounts[level][day][event]);
}
}
}
@ -73,11 +74,22 @@ try {
for (day in levelSubscriptionCounts[level]) {
if (today === day) continue; // Never save data for today because it's incomplete
for (event in levelSubscriptionCounts[level][day]) {
insertEventCount(event, level, day, levelSubscriptionCounts[level][day][event]);
insertLevelEventCount(event, level, day, levelSubscriptionCounts[level][day][event]);
}
}
}
log("Getting active user counts...");
var activeUserCounts = getActiveUserCounts(startDay, activeUserEvents);
// printjson(activeUserCounts);
log("Inserting active user counts...");
for (day in activeUserCounts) {
if (today === day) continue; // Never save data for today because it's incomplete
for (event in activeUserCounts[day]) {
insertEventCount(event, day, activeUserCounts[day][event]);
}
}
log("Script runtime: " + (new Date() - scriptStartTime));
}
catch(err) {
@ -383,7 +395,87 @@ function getLevelSubscriptionCounts(startDay) {
return levelFunnelData;
}
function insertEventCount(event, level, day, count) {
function getActiveUserCounts(startDay, activeUserEvents) {
// Counts active users per day
if (!startDay) return {};
var startObj = objectIdWithTimestamp(ISODate(startDay + "T00:00:00.000Z"));
var queryParams = {$and: [
{_id: {$gte: startObj}},
{'event': {$in: activeUserEvents}}
]};
var cursor = logDB['log'].find(queryParams);
var dayUserMap = {};
while (cursor.hasNext()) {
var doc = cursor.next();
var created = doc._id.getTimestamp().toISOString();
var day = created.substring(0, 10);
var user = doc.user;
if (!dayUserMap[day]) dayUserMap[day] = {};
dayUserMap[day][user] = true;
}
// printjson(dayUserMap['2015-11-01']);
var activeUsersCounts = {};
var monthlyActives = [];
for (day in dayUserMap) {
activeUsersCounts[day] = {'Daily Active Users': Object.keys(dayUserMap[day]).length};
monthlyActives.push({day: day, users: dayUserMap[day]});
}
monthlyActives.sort(function (a, b) {return a.day.localeCompare(b.day);});
// Calculate monthly actives for each day, starting when we have enough data
for (var i = daysInMonth - 1; i < monthlyActives.length; i++) {
var monthUserMap = {};
for (var j = i - daysInMonth + 1; j <= i; j++) {
for (var user in monthlyActives[j].users) {
monthUserMap[user] = true;
}
}
activeUsersCounts[monthlyActives[i].day]['Monthly Active Users'] = Object.keys(monthUserMap).length;
}
return activeUsersCounts;
}
function insertEventCount(event, day, count) {
// analytics.perdays schema in server/analytics/AnalyticsPeryDay.coffee
day = day.replace(/-/g, '');
var eventID = getAnalyticsString(event);
var filterID = getAnalyticsString('all');
var startObj = objectIdWithTimestamp(ISODate(startDay + "T00:00:00.000Z"));
var queryParams = {$and: [{d: day}, {e: eventID}, {f: filterID}]};
var doc = db['analytics.perdays'].findOne(queryParams);
if (doc && doc.c === count) return;
if (doc && doc.c !== count) {
// Update existing count, assume new one is more accurate
// log("Updating count in db for " + day + " " + event + " " + doc.c + " => " + count);
var results = db['analytics.perdays'].update(queryParams, {$set: {c: count}});
if (results.nMatched !== 1 && results.nModified !== 1) {
log("ERROR: update event count failed");
printjson(results);
}
}
else {
var insertDoc = {d: day, e: eventID, f: filterID, c: count};
var results = db['analytics.perdays'].insert(insertDoc);
if (results.nInserted !== 1) {
log("ERROR: insert event failed");
printjson(results);
printjson(insertDoc);
}
// else {
// log("Added " + day + " " + event + " " + count);
// }
}
}
function insertLevelEventCount(event, level, day, count) {
// analytics.perdays schema in server/analytics/AnalyticsPeryDay.coffee
day = day.replace(/-/g, '');