// Level completion counts broken down into free and paid buckets // Usage: // mongo <address>:<port>/<database> <script file> -u <username> -p <password> // TODO: subscriber is someone who is currently subscribed, not necessarily subscribed when they completed a level // Excluded for one reason or another // Some relevant code: https://github.com/codecombat/codecombat/blob/master/app/views/play/CampaignView.coffee#L281-L292 var excludedLevels = ['deadly-dungeon-rescue', 'kithgard-brawl', 'cavern-survival', 'kithgard-mastery', 'destroying-angel', 'kithgard-apprentice', 'wild-horses', 'lost-viking', 'forest-flower-grove', 'boulder-woods', 'the-trials']; var scriptStartTime = new Date(); var startDay = '2015-05-16'; var endDay = '2015-06-17'; log("Dates: " + startDay + " to " + endDay); var subscribers = getSubscribers(); log("Subscriber count: " + Object.keys(subscribers).length); var campaigns = getCampaigns(); for (var i = 0; i < campaigns.length; i++) { var campaign = campaigns[i]; // if (campaign.slug !== 'mountain') continue; function printCampaign(title, prop) { print(title) print("Total\tFree\tSubscribers"); for (var j = 0; j < campaign[prop].length; j++) { var levelSlug = campaign[prop][j]; if (excludedLevels.indexOf(levelSlug) >= 0) continue; var data = getCompletionCounts([levelSlug], subscribers); if (data[levelSlug]) { var free = data[levelSlug].free.length; var paid = data[levelSlug].paid.length; var total = free + paid; var paidRate = parseInt(paid / total * 100); print(total + "\t" + free + "\t" + paid + "\t\t" + paidRate + "%\t" + levelSlug); } else { print("0\t0\t0\t\t0%\t" + levelSlug); } } } printCampaign(campaign.slug + " (free)", "free"); printCampaign(campaign.slug + " (paid)", "paid"); printCampaign(campaign.slug + " (replayable)", "replayable"); // break; } log("Script runtime: " + (new Date() - scriptStartTime)); function log(str) { print(new Date().toISOString() + " " + str); } function objectIdWithTimestamp(timestamp) { // Convert string date to Date object (otherwise assume timestamp is a date) if (typeof(timestamp) == 'string') timestamp = new Date(timestamp); // Convert date object to hex seconds since Unix epoch var hexSeconds = Math.floor(timestamp/1000).toString(16); // Create an ObjectId with that hex timestamp var constructedObjectId = ObjectId(hexSeconds + "0000000000000000"); return constructedObjectId } function getCampaigns() { var campaigns = []; var cursor = db.campaigns.find({}, {slug: 1, levels: 1}); var allFree = 0; var allpaid = 0; while (cursor.hasNext()) { var doc = cursor.next(); if (doc.slug === 'auditions') continue; var campaign = {slug: doc.slug, free: [], paid: [], replayable: []}; for (var levelID in doc.levels) { if (doc.levels[levelID].replayable) { campaign.replayable.push(doc.levels[levelID].slug); } else if (doc.levels[levelID].requiresSubscription) { campaign.paid.push(doc.levels[levelID].slug); } else { campaign.free.push(doc.levels[levelID].slug); } } campaigns.push(campaign); } return campaigns; } function getCompletionCounts(levelSlugs, subscribers) { var startObj = objectIdWithTimestamp(ISODate(startDay + "T00:00:00.000Z")); var endObj = objectIdWithTimestamp(ISODate(endDay + "T00:00:00.000Z")) var cursor = db['level.sessions'].find({ $and: [ {"state.complete": true}, {levelID: {$in: levelSlugs}}, {_id: {$gte: startObj}}, {_id: {$lt: endObj}} ] }); var completionCounts = {}; while (cursor.hasNext()) { var myDoc = cursor.next(); var userID = myDoc.creator; var levelID = myDoc.levelID; if (!completionCounts[levelID]) completionCounts[levelID] = {free: [], paid: []}; if (subscribers[userID]) { completionCounts[levelID].paid.push(myDoc._id.valueOf()); } else { completionCounts[levelID].free.push(myDoc._id.valueOf()); } } return completionCounts; } function getSubscribers() { var cursor = db['users'].find({ $and: [ { $or: [ {"stripe.sponsorID": {$exists: true}}, {$and: [ {"stripe.subscriptionID": {$exists: true}}, {"stripe.planID": 'basic'} ] } ] }, {permissions: {$ne: ['admin']}}, {"stripe.free": {$exists: false}}, {"stripe.coupon": {$exists: false}}, {"stripe.prepaidCode": {$exists: false}} ] }); var subscribers = {}; while (cursor.hasNext()) { subscribers[cursor.next()._id.valueOf()] = true; } return subscribers; }