From e471d652e5ac37f4ea1a9929d78b720189bd2edd Mon Sep 17 00:00:00 2001
From: Matt Lott <mattlott@live.com>
Date: Mon, 22 Feb 2016 12:05:29 -0800
Subject: [PATCH] Add more year long dashboard graphs

---
 app/templates/admin/analytics.jade   |  40 ++++---
 app/views/admin/AnalyticsView.coffee | 160 ++++++++++++---------------
 2 files changed, 97 insertions(+), 103 deletions(-)

diff --git a/app/templates/admin/analytics.jade b/app/templates/admin/analytics.jade
index afbe31362..741bc5971 100644
--- a/app/templates/admin/analytics.jade
+++ b/app/templates/admin/analytics.jade
@@ -8,28 +8,28 @@ block content
     .container-fluid
       .row
         .col-md-5.big-stat.active-classes
+          div.description Monthly Active Classes
           if activeClasses.length > 0
-            div.description Monthly Active Classes
             div.count= activeClasses[0].groups[activeClasses[0].groups.length - 1]
         .col-md-5.big-stat.recurring-revenue
+          div.description Monthly Recurring Revenue
           if revenue.length > 0
-            div.description Monthly Recurring Revenue
             div.count $#{Math.round((revenue[0].groups[revenue[0].groups.length - 1]) / 100)}
         .col-md-5.big-stat.classroom-active-users
+          div.description Classroom Monthly Active Users
           if activeUsers.length > 0
             - var classroomBigMAU = 0; 
             each count, event in activeUsers[0].events
               if event.indexOf('MAU classroom') >= 0
                 - classroomBigMAU += count;
-            div.description Classroom Monthly Active Users
             div.count= classroomBigMAU
         .col-md-5.big-stat.campaign-active-users
+          div.description Campaign Monthly Active Users
           if activeUsers.length > 0
             - var campaignBigMAU = 0; 
             each count, event in activeUsers[0].events
               if event.indexOf('MAU campaign') >= 0
                 - campaignBigMAU += count;
-            div.description Campaign Monthly Active Users
             div.count= campaignBigMAU
 
     ul.nav.nav-tabs
@@ -62,7 +62,10 @@ block content
         .small Paid class: at least one paid student in the classroom
         .small Trial class: not paid, at least one trial student in classroom
         .small Free class: not paid, not trial
-        .active-classes-chart.line-chart-container
+        .active-classes-chart-90.line-chart-container
+
+        h3 Active Classes 365 days
+        .active-classes-chart-365.line-chart-container
 
         h1#active-classes-table Active Classes
         table.table.table-striped.table-condensed
@@ -93,7 +96,7 @@ block content
         h1#recurring-revenue-table Recurring Revenue
         table.table.table-striped.table-condensed
           tr
-            th Day
+            th(style='min-width:85px;') Day
             for group in revenueGroups
               th= group.replace('DRR ', 'Daily ').replace('MRR ', 'Monthly ')
           each entry in revenue
@@ -107,10 +110,16 @@ block content
         .small Paid student: user.coursePrepaidID set and prepaid.properties.trialRequestID NOT set
         .small Trial student: user.coursePrepaidID set and prepaid.properties.trialRequestID set
         .small Free student: not paid, not trial
-        .classroom-daily-active-users-chart.line-chart-container
+        .classroom-daily-active-users-chart-90.line-chart-container
 
         h3#classroom-maus-graph Classroom Monthly Active Users 90 days
-        .classroom-monthly-active-users-chart.line-chart-container
+        .classroom-monthly-active-users-chart-90.line-chart-container
+
+        h3#classroom-daus-graph Classroom Daily Active Users 365 days
+        .classroom-daily-active-users-chart-365.line-chart-container
+
+        h3#classroom-maus-graph Classroom Monthly Active Users 365 days
+        .classroom-monthly-active-users-chart-365.line-chart-container
 
         h3#enrollments-graph Enrollments Issued and Redeemed 90 days
         .paid-courses-chart.line-chart-container
@@ -199,13 +208,19 @@ block content
                 td 0
 
       .tab-pane#tab_campaign
-        h3#campaign-daus-graph Campaign Daily Active Users 90 days
+        h3 Campaign Daily Active Users 90 days
         .small Paid user: had monthly or yearly sub on given day
         .small Free user: not paid
-        .campaign-daily-active-users-chart.line-chart-container
+        .campaign-daily-active-users-chart-90.line-chart-container
 
-        h3#campaign-maus-graph Campaign Monthly Active Users 90 days
-        .campaign-monthly-active-users-chart.line-chart-container
+        h3 Campaign Monthly Active Users 90 days
+        .campaign-monthly-active-users-chart-90.line-chart-container
+
+        h3 Campaign Daily Active Users 365 days
+        .campaign-daily-active-users-chart-365.line-chart-container
+
+        h3 Campaign Monthly Active Users 365 days
+        .campaign-monthly-active-users-chart-365.line-chart-container
 
         h1#active-users-table Active Users
         if activeUsers.length > 0
@@ -236,7 +251,6 @@ block content
         .campaign-vs-classroom-monthly-active-users-recent-chart.line-chart-container
 
         h3#campaign-vs-classroom-paid-maus-graph Campaign vs Classroom Paid Monthly Active Users 365 days
-        .small TODO: aggregate active user data from last year
         .campaign-vs-classroom-monthly-active-users-chart.line-chart-container
 
         h1#active-users-table Active Users
diff --git a/app/views/admin/AnalyticsView.coffee b/app/views/admin/AnalyticsView.coffee
index 0ba3ba279..66a4e7111 100644
--- a/app/views/admin/AnalyticsView.coffee
+++ b/app/views/admin/AnalyticsView.coffee
@@ -287,11 +287,16 @@ module.exports = class AnalyticsView extends RootView
     visibleWidth = $('.kpi-recent-chart').width()
     d3Utils.createLineChart('.kpi-recent-chart', @kpiRecentChartLines, visibleWidth)
     d3Utils.createLineChart('.kpi-chart', @kpiChartLines, visibleWidth)
-    d3Utils.createLineChart('.active-classes-chart', @activeClassesChartLines, visibleWidth)
-    d3Utils.createLineChart('.classroom-daily-active-users-chart', @classroomDailyActiveUsersChartLines, visibleWidth)
-    d3Utils.createLineChart('.classroom-monthly-active-users-chart', @classroomMonthlyActiveUsersChartLines, visibleWidth)
-    d3Utils.createLineChart('.campaign-daily-active-users-chart', @campaignDailyActiveUsersChartLines, visibleWidth)
-    d3Utils.createLineChart('.campaign-monthly-active-users-chart', @campaignMonthlyActiveUsersChartLines, visibleWidth)
+    d3Utils.createLineChart('.active-classes-chart-90', @activeClassesChartLines90, visibleWidth)
+    d3Utils.createLineChart('.active-classes-chart-365', @activeClassesChartLines365, visibleWidth)
+    d3Utils.createLineChart('.classroom-daily-active-users-chart-90', @classroomDailyActiveUsersChartLines90, visibleWidth)
+    d3Utils.createLineChart('.classroom-monthly-active-users-chart-90', @classroomMonthlyActiveUsersChartLines90, visibleWidth)
+    d3Utils.createLineChart('.classroom-daily-active-users-chart-365', @classroomDailyActiveUsersChartLines365, visibleWidth)
+    d3Utils.createLineChart('.classroom-monthly-active-users-chart-365', @classroomMonthlyActiveUsersChartLines365, visibleWidth)
+    d3Utils.createLineChart('.campaign-daily-active-users-chart-90', @campaignDailyActiveUsersChartLines90, visibleWidth)
+    d3Utils.createLineChart('.campaign-monthly-active-users-chart-90', @campaignMonthlyActiveUsersChartLines90, visibleWidth)
+    d3Utils.createLineChart('.campaign-daily-active-users-chart-365', @campaignDailyActiveUsersChartLines365, visibleWidth)
+    d3Utils.createLineChart('.campaign-monthly-active-users-chart-365', @campaignMonthlyActiveUsersChartLines365, visibleWidth)
     d3Utils.createLineChart('.campaign-vs-classroom-monthly-active-users-recent-chart.line-chart-container', @campaignVsClassroomMonthlyActiveUsersRecentChartLines, visibleWidth)
     d3Utils.createLineChart('.campaign-vs-classroom-monthly-active-users-chart.line-chart-container', @campaignVsClassroomMonthlyActiveUsersChartLines, visibleWidth)
     d3Utils.createLineChart('.paid-courses-chart', @enrollmentsChartLines, visibleWidth)
@@ -393,9 +398,9 @@ module.exports = class AnalyticsView extends RootView
         showYScale: true
 
   updateActiveClassesChartData: ->
-    @activeClassesChartLines = []
+    @activeClassesChartLines90 = []
+    @activeClassesChartLines365 = []
     return unless @activeClasses?.length
-    days = d3Utils.createContiguousDays(90)
 
     groupDayMap = {}
     for entry in @activeClasses
@@ -404,35 +409,42 @@ module.exports = class AnalyticsView extends RootView
         groupDayMap[@activeClassGroups[i]][entry.day] ?= 0
         groupDayMap[@activeClassGroups[i]][entry.day] += count
 
-    lines = []
-    colorIndex = 0
-    totalMax = 0
-    for group, entries of groupDayMap
-      data = []
-      for day, count of entries
-        data.push
-          day: day
-          value: count
-      data.reverse()
-      points = @createLineChartPoints(days, data)
-      @activeClassesChartLines.push
-        points: points
-        description: group.replace('Active classes ', '')
-        lineColor: @lineColors[colorIndex++ % @lineColors.length]
-        strokeWidth: 1
-        min: 0
-        showYScale: group is 'Total'
-      totalMax = _.max(points, 'y').y if group is 'Total'
-    line.max = totalMax for line in @activeClassesChartLines
+    createActiveClassesChartLines = (lines, numDays) =>
+      days = d3Utils.createContiguousDays(numDays)
+      colorIndex = 0
+      totalMax = 0
+      for group, entries of groupDayMap
+        data = []
+        for day, count of entries
+          data.push
+            day: day
+            value: count
+        data.reverse()
+        points = @createLineChartPoints(days, data)
+        lines.push
+          points: points
+          description: group.replace('Active classes ', '')
+          lineColor: @lineColors[colorIndex++ % @lineColors.length]
+          strokeWidth: 1
+          min: 0
+          showYScale: group is 'Total'
+        totalMax = _.max(points, 'y').y if group is 'Total'
+      line.max = totalMax for line in lines
+
+    createActiveClassesChartLines(@activeClassesChartLines90, 90)
+    createActiveClassesChartLines(@activeClassesChartLines365, 365)
 
   updateActiveUsersChartData: ->
     # Create chart lines for the active user events returned by active_users in analytics_perday_handler
-    @campaignDailyActiveUsersChartLines = []
-    @campaignMonthlyActiveUsersChartLines = []
-    @classroomDailyActiveUsersChartLines = []
-    @classroomMonthlyActiveUsersChartLines = []
+    @campaignDailyActiveUsersChartLines90 = []
+    @campaignMonthlyActiveUsersChartLines90 = []
+    @campaignDailyActiveUsersChartLines365 = []
+    @campaignMonthlyActiveUsersChartLines365 = []
+    @classroomDailyActiveUsersChartLines90 = []
+    @classroomMonthlyActiveUsersChartLines90 = []
+    @classroomDailyActiveUsersChartLines365 = []
+    @classroomMonthlyActiveUsersChartLines365 = []
     return unless @activeUsers?.length
-    days = d3Utils.createContiguousDays(90)
 
     # Separate day/value arrays by event
     eventDataMap = {}
@@ -444,65 +456,33 @@ module.exports = class AnalyticsView extends RootView
           day: entry.day
           value: count
 
-    # Build chart lines for each event
-    eventLineMap = 
-      'DAU campaign': {max: 0, colorIndex: 0}
-      'MAU campaign': {max: 0, colorIndex: 0}
-      'DAU classroom': {max: 0, colorIndex: 0}
-      'MAU classroom': {max: 0, colorIndex: 0}
-    for event, data of eventDataMap
-      data.reverse()
-      points = @createLineChartPoints(days, data)
-      max = _.max(points, 'y').y
-      if event.indexOf('DAU campaign') >= 0
-        chartLines = @campaignDailyActiveUsersChartLines
-        eventLineMap['DAU campaign'].max = Math.max(eventLineMap['DAU campaign'].max, max)
-        lineColor = @lineColors[eventLineMap['DAU campaign'].colorIndex++ % @lineColors.length]
-      else if event.indexOf('MAU campaign') >= 0
-        chartLines = @campaignMonthlyActiveUsersChartLines
-        eventLineMap['MAU campaign'].max = Math.max(eventLineMap['MAU campaign'].max, max) 
-        lineColor = @lineColors[eventLineMap['MAU campaign'].colorIndex++ % @lineColors.length]
-      else if event.indexOf('DAU classroom') >= 0
-        chartLines = @classroomDailyActiveUsersChartLines
-        eventLineMap['DAU classroom'].max = Math.max(eventLineMap['DAU classroom'].max, max) 
-        lineColor = @lineColors[eventLineMap['DAU classroom'].colorIndex++ % @lineColors.length]
-      else if event.indexOf('MAU classroom') >= 0
-        chartLines = @classroomMonthlyActiveUsersChartLines 
-        eventLineMap['MAU classroom'].max = Math.max(eventLineMap['MAU classroom'].max, max) 
-        lineColor = @lineColors[eventLineMap['MAU classroom'].colorIndex++ % @lineColors.length]
-      chartLines.push
-        points: points
-        description: event
-        lineColor: lineColor 
-        strokeWidth: 1
-        min: 0
-        showYScale: false
+    createActiveUsersChartLines = (lines, numDays, eventPrefix) =>
+      days = d3Utils.createContiguousDays(numDays)
+      colorIndex = 0
+      lineMax = 0
+      showYScale = true
+      for event, data of eventDataMap
+        continue unless event.indexOf(eventPrefix) >= 0
+        points = @createLineChartPoints(days, _.cloneDeep(data).reverse())
+        lineMax = Math.max(_.max(points, 'y').y, lineMax)
+        lines.push
+          points: points
+          description: event
+          lineColor: @lineColors[colorIndex++ % @lineColors.length] 
+          strokeWidth: 1
+          min: 0
+          showYScale: showYScale
+        showYScale = false
+      line.max = lineMax for line in lines
 
-    # Update line Y scales and maxes
-    showYScaleSet = false
-    for line in @campaignDailyActiveUsersChartLines
-      line.max = eventLineMap['DAU campaign'].max
-      unless showYScaleSet
-        line.showYScale = true
-        showYScaleSet = true 
-    showYScaleSet = false
-    for line in @campaignMonthlyActiveUsersChartLines
-      line.max = eventLineMap['MAU campaign'].max
-      unless showYScaleSet
-        line.showYScale = true
-        showYScaleSet = true
-    showYScaleSet = false
-    for line in @classroomDailyActiveUsersChartLines
-      line.max = eventLineMap['DAU classroom'].max
-      unless showYScaleSet
-        line.showYScale = true
-        showYScaleSet = true 
-    showYScaleSet = false
-    for line in @classroomMonthlyActiveUsersChartLines
-      line.max = eventLineMap['MAU classroom'].max
-      unless showYScaleSet
-        line.showYScale = true
-        showYScaleSet = true 
+    createActiveUsersChartLines(@campaignDailyActiveUsersChartLines90, 90, 'DAU campaign')
+    createActiveUsersChartLines(@campaignMonthlyActiveUsersChartLines90, 90, 'MAU campaign')
+    createActiveUsersChartLines(@classroomDailyActiveUsersChartLines90, 90, 'DAU classroom')
+    createActiveUsersChartLines(@classroomMonthlyActiveUsersChartLines90, 90, 'MAU classroom')
+    createActiveUsersChartLines(@campaignDailyActiveUsersChartLines365, 365, 'DAU campaign')
+    createActiveUsersChartLines(@campaignMonthlyActiveUsersChartLines365, 365, 'MAU campaign')
+    createActiveUsersChartLines(@classroomDailyActiveUsersChartLines365, 365, 'DAU classroom')
+    createActiveUsersChartLines(@classroomMonthlyActiveUsersChartLines365, 365, 'MAU classroom')
 
   updateCampaignVsClassroomActiveUsersChartData: ->
     @campaignVsClassroomMonthlyActiveUsersRecentChartLines = []