mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-30 19:06:59 -05:00
Add trials to active classes analytics dashboard
Only looking at active classrooms, split into paid, trial, and free. Active class: 12+ students total, 6+ active in last 30 days.
This commit is contained in:
parent
95e627f346
commit
e8c22679d9
3 changed files with 108 additions and 257 deletions
|
@ -27,6 +27,7 @@ block content
|
||||||
.kpi-chart.line-chart-container
|
.kpi-chart.line-chart-container
|
||||||
|
|
||||||
h3 Active Classes 90 days
|
h3 Active Classes 90 days
|
||||||
|
.small Active class: 12+ students in a classroom, with 6+ who played in last 30 days.
|
||||||
.active-classes-chart.line-chart-container
|
.active-classes-chart.line-chart-container
|
||||||
|
|
||||||
h3 Recurring Revenue 90 days
|
h3 Recurring Revenue 90 days
|
||||||
|
|
|
@ -1,31 +1,42 @@
|
||||||
|
/* global db */
|
||||||
|
/* global Mongo */
|
||||||
|
/* global ISODate */
|
||||||
// Insert per-day active class counts into analytics.perdays collection
|
// Insert per-day active class counts into analytics.perdays collection
|
||||||
|
|
||||||
// Usage:
|
// Usage:
|
||||||
// mongo <address>:<port>/<database> <script file> -u <username> -p <password>
|
// mongo <address>:<port>/<database> <script file> -u <username> -p <password>
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logDB = new Mongo("localhost").getDB("analytics")
|
var logDB = new Mongo("localhost").getDB("analytics")
|
||||||
var scriptStartTime = new Date();
|
var scriptStartTime = new Date();
|
||||||
var analyticsStringCache = {};
|
var analyticsStringCache = {};
|
||||||
|
|
||||||
|
var minClassSize = 12;
|
||||||
|
var minActiveCount = 6;
|
||||||
|
|
||||||
|
var eventNamePaid = 'Active classes paid';
|
||||||
|
var eventNameTrial = 'Active classes trial';
|
||||||
|
var eventNameFree = 'Active classes free';
|
||||||
|
|
||||||
var numDays = 40;
|
var numDays = 40;
|
||||||
var daysInMonth = 30;
|
var daysInMonth = 30;
|
||||||
|
|
||||||
var startDay = new Date();
|
var startDay = new Date();
|
||||||
today = startDay.toISOString().substr(0, 10);
|
var today = startDay.toISOString().substr(0, 10);
|
||||||
startDay.setUTCDate(startDay.getUTCDate() - numDays);
|
startDay.setUTCDate(startDay.getUTCDate() - numDays);
|
||||||
startDay = startDay.toISOString().substr(0, 10);
|
startDay = startDay.toISOString().substr(0, 10);
|
||||||
|
|
||||||
log("Today is " + today);
|
log("Today is " + today);
|
||||||
log("Start day is " + startDay);
|
log("Start day is " + startDay);
|
||||||
|
|
||||||
log("Getting active class counts...");
|
log("Getting active class counts..");
|
||||||
var activeClassCounts = getActiveClassCounts(startDay);
|
var activeClassCounts = getActiveClassCounts(startDay);
|
||||||
// printjson(activeClassCounts);
|
// printjson(activeClassCounts);
|
||||||
log("Inserting active class counts...");
|
log("Inserting active class counts..");
|
||||||
for (var event in activeClassCounts) {
|
for (var event in activeClassCounts) {
|
||||||
for (var day in activeClassCounts[event]) {
|
for (var day in activeClassCounts[event]) {
|
||||||
if (today === day) continue; // Never save data for today because it's incomplete
|
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]);
|
insertEventCount(event, day, activeClassCounts[event][day]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,130 +49,73 @@ catch(err) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getActiveClassCounts(startDay) {
|
function getActiveClassCounts(startDay) {
|
||||||
// Tally active classes per day
|
// Tally active classes per day, for paid, trial, and free
|
||||||
// TODO: does not handle class membership changes
|
// TODO: does not handle class membership changes
|
||||||
|
|
||||||
if (!startDay) return {};
|
if (!startDay) return {};
|
||||||
|
|
||||||
var minGroupSize = 12;
|
var cursor, doc;
|
||||||
var classes = {
|
|
||||||
'Active classes private clan': [],
|
|
||||||
'Active classes managed subscription': [],
|
|
||||||
'Active classes bulk subscription': [],
|
|
||||||
'Active classes prepaid': [],
|
|
||||||
'Active classes course free': [],
|
|
||||||
'Active classes course paid': []
|
|
||||||
};
|
|
||||||
var userPlayedMap = {};
|
|
||||||
|
|
||||||
// Private clans
|
|
||||||
// TODO: does not handle clan membership changes over time
|
|
||||||
var cursor = db.clans.find({$and: [{type: 'private'}, {$where: 'this.members.length >= ' + minGroupSize}]});
|
|
||||||
while (cursor.hasNext()) {
|
|
||||||
var doc = cursor.next();
|
|
||||||
var members = doc.members.map(function(a) {
|
|
||||||
userPlayedMap[a.valueOf()] = [];
|
|
||||||
return a.valueOf();
|
|
||||||
});
|
|
||||||
classes['Active classes private clan'].push({
|
|
||||||
owner: doc.ownerID.valueOf(),
|
|
||||||
members: members,
|
|
||||||
activeDayMap: {}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Managed subscriptions
|
|
||||||
// TODO: does not handle former recipients playing after sponsorship ends
|
|
||||||
var bulkSubGroups = {};
|
|
||||||
cursor = db.payments.find({$and: [{service: 'stripe'}, {$where: '!this.purchaser.equals(this.recipient)'}]});
|
|
||||||
while (cursor.hasNext()) {
|
|
||||||
var doc = cursor.next();
|
|
||||||
var purchaser = doc.purchaser.valueOf();
|
|
||||||
if (!bulkSubGroups[purchaser]) bulkSubGroups[purchaser] = {};
|
|
||||||
bulkSubGroups[purchaser][doc.recipient.valueOf()] = true;
|
|
||||||
}
|
|
||||||
for (var purchaser in bulkSubGroups) {
|
|
||||||
if (Object.keys(bulkSubGroups[purchaser]).length >= minGroupSize) {
|
|
||||||
for (var member in bulkSubGroups[purchaser]) {
|
|
||||||
userPlayedMap[member] = [];
|
|
||||||
}
|
|
||||||
classes['Active classes managed subscription'].push({
|
|
||||||
owner: purchaser,
|
|
||||||
members: Object.keys(bulkSubGroups[purchaser]),
|
|
||||||
activeDayMap: {}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bulk subscriptions
|
|
||||||
bulkSubGroups = {};
|
|
||||||
cursor = db.payments.find({$and: [{service: 'external'}, {$where: '!this.purchaser.equals(this.recipient)'}]});
|
|
||||||
while (cursor.hasNext()) {
|
|
||||||
var doc = cursor.next();
|
|
||||||
var purchaser = doc.purchaser.valueOf();
|
|
||||||
if (!bulkSubGroups[purchaser]) bulkSubGroups[purchaser] = {};
|
|
||||||
bulkSubGroups[purchaser][doc.recipient.valueOf()] = true;
|
|
||||||
}
|
|
||||||
for (var purchaser in bulkSubGroups) {
|
|
||||||
if (Object.keys(bulkSubGroups[purchaser]).length >= minGroupSize) {
|
|
||||||
for (var member in bulkSubGroups[purchaser]) {
|
|
||||||
userPlayedMap[member] = [];
|
|
||||||
}
|
|
||||||
classes['Active classes bulk subscription'].push({
|
|
||||||
owner: purchaser,
|
|
||||||
members: Object.keys(bulkSubGroups[purchaser]),
|
|
||||||
activeDayMap: {}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepaids terminal_subscription
|
|
||||||
bulkSubGroups = {};
|
|
||||||
cursor = db.prepaids.find(
|
|
||||||
{$and: [{type: 'terminal_subscription'}, {$where: 'this.redeemers && this.redeemers.length >= ' + minGroupSize}]},
|
|
||||||
{creator: 1, type: 1, redeemers: 1}
|
|
||||||
);
|
|
||||||
while (cursor.hasNext()) {
|
|
||||||
var doc = cursor.next();
|
|
||||||
var owner = doc.creator.valueOf();
|
|
||||||
var members = [];
|
|
||||||
for (var i = 0 ; i < doc.redeemers.length; i++) {
|
|
||||||
userPlayedMap[doc.redeemers[i].userID.valueOf()] = [];
|
|
||||||
members.push(doc.redeemers[i].userID.valueOf());
|
|
||||||
}
|
|
||||||
classes['Active classes prepaid'].push({
|
|
||||||
owner: owner,
|
|
||||||
members: members,
|
|
||||||
activeDayMap: {}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Classrooms
|
// Classrooms
|
||||||
var classroomCourseInstancesMap = {};
|
// paid: at least one paid member
|
||||||
cursor = db.course.instances.find(
|
// free trial: not paid, at least one trial member
|
||||||
{$where: 'this.members && this.members.length >= ' + minGroupSize},
|
// free: not paid, not free trial
|
||||||
{classroomID: 1, courseID: 1, members: 1, ownerID: 1}
|
// user.coursePrepaidID set means access to paid courses
|
||||||
);
|
// prepaid.properties.trialRequestID means access was via trial
|
||||||
|
|
||||||
|
// Find classroom users
|
||||||
|
log("Finding classrooms..");
|
||||||
|
var userClassroomsMap = {};
|
||||||
|
var classroomUsersMap = {};
|
||||||
|
var classroomUserIDs = [];
|
||||||
|
var classroomUserObjectIds = [];
|
||||||
|
cursor = db.classrooms.find({}, {members: 1});
|
||||||
while (cursor.hasNext()) {
|
while (cursor.hasNext()) {
|
||||||
var doc = cursor.next();
|
doc = cursor.next();
|
||||||
var owner = doc.ownerID.valueOf();
|
if (doc.members) {
|
||||||
var classroom = doc.classroomID ? doc.classroomID.valueOf() : doc._id.valueOf();
|
var classroomID = doc._id.valueOf();
|
||||||
var members = [];
|
for (var i = 0; i < doc.members.length; i++) {
|
||||||
for (var i = 0 ; i < doc.members.length; i++) {
|
if (doc.members.length < minClassSize) continue;
|
||||||
userPlayedMap[doc.members[i].valueOf()] = [];
|
var userID = doc.members[i].valueOf();
|
||||||
members.push(doc.members[i].valueOf());
|
if (!userClassroomsMap[userID]) userClassroomsMap[userID] = [];
|
||||||
|
userClassroomsMap[userID].push(classroomID);
|
||||||
|
if (!classroomUsersMap[classroomID]) classroomUsersMap[classroomID] = [];
|
||||||
|
classroomUsersMap[classroomID].push(userID)
|
||||||
|
classroomUserIDs.push(doc.members[i].valueOf());
|
||||||
|
classroomUserObjectIds.push(doc.members[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!classroomCourseInstancesMap[classroom]) classroomCourseInstancesMap[classroom] = [];
|
|
||||||
classroomCourseInstancesMap[classroom].push({
|
|
||||||
course: doc.courseID.valueOf(),
|
|
||||||
owner: owner,
|
|
||||||
members: members,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// printjson(classroomCourseInstancesMap);
|
log("Find user types..");
|
||||||
|
var userEventMap = {};
|
||||||
|
var prepaidUsersMap = {};
|
||||||
|
var prepaidIDs = [];
|
||||||
|
cursor = db.users.find({_id: {$in: classroomUserObjectIds}}, {coursePrepaidID: 1});
|
||||||
|
while (cursor.hasNext()) {
|
||||||
|
doc = cursor.next();
|
||||||
|
if (doc.coursePrepaidID) {
|
||||||
|
userEventMap[doc._id.valueOf()] = eventNamePaid;
|
||||||
|
if (!prepaidUsersMap[doc.coursePrepaidID.valueOf()]) prepaidUsersMap[doc.coursePrepaidID.valueOf()] = [];
|
||||||
|
prepaidUsersMap[doc.coursePrepaidID.valueOf()].push(doc._id.valueOf());
|
||||||
|
prepaidIDs.push(doc.coursePrepaidID);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
userEventMap[doc._id.valueOf()] = eventNameFree;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cursor = db.prepaids.find({_id: {$in: prepaidIDs}}, {properties: 1});
|
||||||
|
while (cursor.hasNext()) {
|
||||||
|
doc = cursor.next();
|
||||||
|
if (doc.properties && doc.properties.trialRequestID) {
|
||||||
|
for (var i = 0; i < prepaidUsersMap[doc._id.valueOf()].length; i++) {
|
||||||
|
userEventMap[prepaidUsersMap[doc._id.valueOf()][i]] = eventNameTrial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Find all the started level events for our class members, for startDay - daysInMonth
|
log("Find Started Level log events for all classroom members for last " + (numDays + daysInMonth) + " days..");
|
||||||
|
var userPlayedMap = {};
|
||||||
var startDate = ISODate(startDay + "T00:00:00.000Z");
|
var startDate = ISODate(startDay + "T00:00:00.000Z");
|
||||||
startDate.setUTCDate(startDate.getUTCDate() - daysInMonth);
|
startDate.setUTCDate(startDate.getUTCDate() - daysInMonth);
|
||||||
var endDate = ISODate(startDay + "T00:00:00.000Z");
|
var endDate = ISODate(startDay + "T00:00:00.000Z");
|
||||||
|
@ -169,162 +123,72 @@ function getActiveClassCounts(startDay) {
|
||||||
var startObj = objectIdWithTimestamp(startDate);
|
var startObj = objectIdWithTimestamp(startDate);
|
||||||
var queryParams = {$and: [
|
var queryParams = {$and: [
|
||||||
{_id: {$gte: startObj}},
|
{_id: {$gte: startObj}},
|
||||||
{user: {$in: Object.keys(userPlayedMap)}},
|
{user: {$in: classroomUserIDs}},
|
||||||
{event: 'Started Level'}
|
{event: 'Started Level'}
|
||||||
]};
|
]};
|
||||||
cursor = logDB['log'].find(queryParams, {user: 1});
|
cursor = logDB['log'].find(queryParams, {user: 1});
|
||||||
while (cursor.hasNext()) {
|
while (cursor.hasNext()) {
|
||||||
var doc = cursor.next();
|
doc = cursor.next();
|
||||||
|
if (!userPlayedMap[doc.user]) userPlayedMap[doc.user] = [];
|
||||||
userPlayedMap[doc.user].push(doc._id.getTimestamp());
|
userPlayedMap[doc.user].push(doc._id.getTimestamp());
|
||||||
}
|
}
|
||||||
|
|
||||||
// printjson(userPlayedMap);
|
// printjson(userPlayedMap);
|
||||||
// print(startDate, endDate, todayDate);
|
|
||||||
|
|
||||||
// Now we have a set of classes, and when users played
|
|
||||||
// For a given day, walk classes and find out how many members were active during the previous daysInMonth
|
|
||||||
while (endDate < todayDate) {
|
|
||||||
var endDay = endDate.toISOString().substring(0, 10);
|
|
||||||
|
|
||||||
// For each class
|
log("Calculate number of active members per classroom per day per event type..");
|
||||||
for (var event in classes) {
|
var classDayTypeMap = {};
|
||||||
for (var i = 0; i < classes[event].length; i++) {
|
for (var classroom in classroomUsersMap) {
|
||||||
|
if (classroomUsersMap[classroom].length < minClassSize) continue;
|
||||||
// For each member of current class
|
|
||||||
var activeMemberCount = 0;
|
|
||||||
for (var j = 0; j < classes[event][i].members.length; j++) {
|
|
||||||
var member = classes[event][i].members[j];
|
|
||||||
|
|
||||||
// Was member active during current timeframe?
|
|
||||||
if (userPlayedMap[member]) {
|
|
||||||
for (var k = 0; k < userPlayedMap[member].length; k++) {
|
|
||||||
if (userPlayedMap[member][k] > startDate && userPlayedMap[member][k] <= endDate) {
|
|
||||||
activeMemberCount++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Classes active for a given day if has minGroupSize members, and at least 1/2 played in last daysInMonth days
|
|
||||||
if (activeMemberCount >= Math.round(classes[event][i].members.length / 2)) {
|
|
||||||
classes[event][i].activeDayMap[endDay] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
startDate.setUTCDate(startDate.getUTCDate() + 1);
|
|
||||||
endDate.setUTCDate(endDate.getUTCDate() + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Classrooms are processed differently because they could be free or paid active classes
|
|
||||||
var courseNameMap = {};
|
|
||||||
cursor = db.courses.find({}, {name: 1});
|
|
||||||
while (cursor.hasNext()) {
|
|
||||||
var doc = cursor.next();
|
|
||||||
courseNameMap[doc._id.valueOf()] = doc.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For each classroom, check free and paid members separately
|
|
||||||
for (var classroom in classroomCourseInstancesMap) {
|
|
||||||
var freeMembers = {};
|
|
||||||
var paidMembers = {};
|
|
||||||
var owner = null;
|
|
||||||
for (var i = 0; i < classroomCourseInstancesMap[classroom].length; i++) {
|
|
||||||
var courseInstance = classroomCourseInstancesMap[classroom][i];
|
|
||||||
if (!owner) owner = courseInstance.owner;
|
|
||||||
for (var j = 0; j < courseInstance.members.length; j++) {
|
|
||||||
if (courseNameMap[courseInstance.course] === 'Introduction to Computer Science') {
|
|
||||||
freeMembers[courseInstance.members[j]] = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
paidMembers[courseInstance.members[j]] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var freeClass = {
|
|
||||||
owner: owner,
|
|
||||||
members: Object.keys(freeMembers),
|
|
||||||
activeDayMap: {}
|
|
||||||
};
|
|
||||||
var paidClass = {
|
|
||||||
owner: owner,
|
|
||||||
members: Object.keys(paidMembers),
|
|
||||||
activeDayMap: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// print('Processing classroom', classroom, freeClass.members.length, paidClass.members.length);
|
|
||||||
|
|
||||||
|
// For each each day in our target date range
|
||||||
|
classDayTypeMap[classroom] = {};
|
||||||
startDate = ISODate(startDay + "T00:00:00.000Z");
|
startDate = ISODate(startDay + "T00:00:00.000Z");
|
||||||
startDate.setUTCDate(startDate.getUTCDate() - daysInMonth);
|
startDate.setUTCDate(startDate.getUTCDate() - daysInMonth);
|
||||||
endDate = ISODate(startDay + "T00:00:00.000Z");
|
endDate = ISODate(startDay + "T00:00:00.000Z");
|
||||||
while (endDate < todayDate) {
|
while (endDate < todayDate) {
|
||||||
var endDay = endDate.toISOString().substring(0, 10);
|
var endDay = endDate.toISOString().substring(0, 10);
|
||||||
|
classDayTypeMap[classroom][endDay] = {};
|
||||||
|
classDayTypeMap[classroom][endDay][eventNamePaid] = 0;
|
||||||
|
classDayTypeMap[classroom][endDay][eventNameTrial] = 0;
|
||||||
|
classDayTypeMap[classroom][endDay][eventNameFree] = 0;
|
||||||
|
|
||||||
// For each paid member of current class
|
// Count active users of each type for current day
|
||||||
var paidActiveMemberCount = 0;
|
for (var j = 0; j < classroomUsersMap[classroom].length; j++) {
|
||||||
for (var j = 0; j < paidClass.members.length; j++) {
|
var member = classroomUsersMap[classroom][j];
|
||||||
var member = paidClass.members[j];
|
|
||||||
|
|
||||||
// Was member active during current timeframe?
|
// Was member active during current timeframe?
|
||||||
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) {
|
||||||
paidActiveMemberCount++;
|
classDayTypeMap[classroom][endDay][userEventMap[member]]++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Classes active for a given day if has minGroupSize members, and at least 1/2 played in last daysInMonth days
|
|
||||||
if (paidClass.members.length > minGroupSize && paidActiveMemberCount >= Math.round(paidClass.members.length / 2)) {
|
|
||||||
// print('paid classroom', classroom, endDay);
|
|
||||||
paidClass.activeDayMap[endDay] = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// For each free member of current class
|
|
||||||
var freeActiveMemberCount = 0;
|
|
||||||
for (var j = 0; j < freeClass.members.length; j++) {
|
|
||||||
var member = freeClass.members[j];
|
|
||||||
|
|
||||||
// Was member active during current timeframe?
|
|
||||||
if (userPlayedMap[member]) {
|
|
||||||
for (var k = 0; k < userPlayedMap[member].length; k++) {
|
|
||||||
if (userPlayedMap[member][k] > startDate && userPlayedMap[member][k] <= endDate) {
|
|
||||||
freeActiveMemberCount++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (freeClass.members.length > minGroupSize && freeActiveMemberCount >= Math.round(freeClass.members.length / 2)) {
|
|
||||||
// print('free classroom', classroom, endDay);
|
|
||||||
freeClass.activeDayMap[endDay] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
startDate.setUTCDate(startDate.getUTCDate() + 1);
|
startDate.setUTCDate(startDate.getUTCDate() + 1);
|
||||||
endDate.setUTCDate(endDate.getUTCDate() + 1);
|
endDate.setUTCDate(endDate.getUTCDate() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// printjson(freeClass);
|
|
||||||
// printjson(paidClass);
|
|
||||||
|
|
||||||
classes['Active classes course free'].push(freeClass);
|
|
||||||
classes['Active classes course paid'].push(paidClass);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// printjson(classes['Active classes course paid']);
|
log("Aggregate class counts by day and type..");
|
||||||
|
|
||||||
var activeClassCounts = {};
|
var activeClassCounts = {};
|
||||||
for (var event in classes) {
|
for (var classroom in classDayTypeMap) {
|
||||||
if (!activeClassCounts[event]) activeClassCounts[event] = {};
|
for (var endDay in classDayTypeMap[classroom]) {
|
||||||
for (var i = 0; i < classes[event].length; i++) {
|
var activeStudents = 0;
|
||||||
for (var endDay in classes[event][i].activeDayMap) {
|
var classEvent = eventNameFree;
|
||||||
if (!activeClassCounts[event][endDay]) activeClassCounts[event][endDay] = 0;
|
for (var event in classDayTypeMap[classroom][endDay]) {
|
||||||
activeClassCounts[event][endDay]++;
|
if (classDayTypeMap[classroom][endDay][event] > 1) {
|
||||||
|
activeStudents += classDayTypeMap[classroom][endDay][event];
|
||||||
|
if (event === eventNamePaid) classEvent = event;
|
||||||
|
if (classEvent !== eventNamePaid && event === eventNameTrial) classEvent = event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (activeStudents >= minActiveCount) {
|
||||||
|
if (!activeClassCounts[classEvent]) activeClassCounts[classEvent] = {};
|
||||||
|
if (!activeClassCounts[classEvent][endDay]) activeClassCounts[classEvent][endDay] = 0;
|
||||||
|
activeClassCounts[classEvent][endDay]++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -334,17 +198,6 @@ function getActiveClassCounts(startDay) {
|
||||||
|
|
||||||
// *** Helper functions ***
|
// *** Helper functions ***
|
||||||
|
|
||||||
function slugify(text)
|
|
||||||
// https://gist.github.com/mathewbyrne/1280286
|
|
||||||
{
|
|
||||||
return text.toString().toLowerCase()
|
|
||||||
.replace(/\s+/g, '-') // Replace spaces with -
|
|
||||||
.replace(/[^\w\-]+/g, '') // Remove all non-word chars
|
|
||||||
.replace(/\-\-+/g, '-') // Replace multiple - with single -
|
|
||||||
.replace(/^-+/, '') // Trim - from start of text
|
|
||||||
.replace(/-+$/, ''); // Trim - from end of text
|
|
||||||
}
|
|
||||||
|
|
||||||
function log(str) {
|
function log(str) {
|
||||||
print(new Date().toISOString() + " " + str);
|
print(new Date().toISOString() + " " + str);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,12 +26,9 @@ class AnalyticsPerDayHandler extends Handler
|
||||||
|
|
||||||
getActiveClasses: (req, res) ->
|
getActiveClasses: (req, res) ->
|
||||||
events = [
|
events = [
|
||||||
'Active classes private clan',
|
'Active classes paid',
|
||||||
'Active classes managed subscription',
|
'Active classes trial',
|
||||||
'Active classes bulk subscription',
|
'Active classes free'
|
||||||
'Active classes prepaid',
|
|
||||||
'Active classes course free',
|
|
||||||
'Active classes course paid'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
AnalyticsString.find({v: {$in: events}}).exec (err, documents) =>
|
AnalyticsString.find({v: {$in: events}}).exec (err, documents) =>
|
||||||
|
|
Loading…
Reference in a new issue