diff --git a/app/core/d3_utils.coffee b/app/core/d3_utils.coffee index 3315764a8..31ff7109d 100644 --- a/app/core/d3_utils.coffee +++ b/app/core/d3_utils.coffee @@ -12,7 +12,7 @@ module.exports.createContiguousDays = (timeframeDays, skipToday=true) -> currentDate.setUTCDate(currentDate.getUTCDate() + 1) days -module.exports.createLineChart = (containerSelector, chartLines) -> +module.exports.createLineChart = (containerSelector, chartLines, containerWidth) -> # Creates a line chart within 'containerSelector' based on chartLines return unless chartLines?.length > 0 and containerSelector @@ -20,7 +20,7 @@ module.exports.createLineChart = (containerSelector, chartLines) -> keyHeight = 20 xAxisHeight = 20 yAxisWidth = 40 - containerWidth = $(containerSelector).width() + containerWidth = $(containerSelector).width() unless containerWidth containerHeight = $(containerSelector).height() yScaleCount = 0 diff --git a/app/styles/admin/analytics.sass b/app/styles/admin/analytics.sass index 1dc7a37d2..4eda7f502 100644 --- a/app/styles/admin/analytics.sass +++ b/app/styles/admin/analytics.sass @@ -1,5 +1,16 @@ #admin-analytics-view + // Force compact top site chrome + background-position: center -226px + padding-top: 50px + #site-nav + top: -80px + #nav-logo + display: none + #small-nav-logo + display: inline-block + height: 30px + #site-content-area width: 100% .big-stat diff --git a/app/templates/admin/analytics.jade b/app/templates/admin/analytics.jade index 09b549f8e..afbe31362 100644 --- a/app/templates/admin/analytics.jade +++ b/app/templates/admin/analytics.jade @@ -32,201 +32,246 @@ block content div.description Campaign Monthly Active Users div.count= campaignBigMAU - h3 KPI 60 days - .kpi-recent-chart.line-chart-container + ul.nav.nav-tabs + li.active + a(data-target="#tab_kpis", data-toggle="tab") KPIs + li + a(data-target="#tab_active_classes", data-toggle="tab") Active Classes + li + a(data-target="#tab_revenue", data-toggle="tab") Revenue + li + a(data-target="#tab_classroom", data-toggle="tab") Classroom + li + a(data-target="#tab_campaign", data-toggle="tab") Campaign + li + a(data-target="#tab_campaign_vs_classroom", data-toggle="tab") Campaign vs Classroom - h3 KPI 365 days - .kpi-chart.line-chart-container + .tab-content + .tab-pane.active#tab_kpis + h3 KPI 60 days + .kpi-recent-chart.line-chart-container - h1 Table of Contents - b Graphs - div - a(href='#active-classes-graph') Active Classes - div - a(href='#recurring-revenue-graph') Recurring Revenue - div - a(href='#classroom-daus-graph') Classroom Daily Active Users - div - a(href='#classroom-maus-graph') Classroom Monthly Active Users - div - a(href='#campaign-daus-graph') Campaign Daily Active Users - div - a(href='#campaign-maus-graph') Campaign Monthly Active Users - div - a(href='#campaign-vs-classroom-paid-maus-recent-graph') Campaign vs Classroom Paid Monthly Active Users (90 days) - div - a(href='#campaign-vs-classroom-paid-maus-graph') Campaign vs Classroom Paid Monthly Active Users (365 days) - div - a(href='#enrollments-graph') Enrollments Issued and Redeemed - b Tables - div - a(href='#furthest-courses-table') Furthest Course - div - a(href='#school-counts-table') School Counts - div - a(href='#active-classes-table') Active Classes - div - a(href='#recurring-revenue-table') Recurring Revenue - div - a(href='#active-users-table') Active Users - div - a(href='#enrollments-table') Enrollments + h3 KPI 365 days + .kpi-chart.line-chart-container - h3#active-classes-graph Active Classes 90 days - .small Active class: 12+ students in a classroom, with 6+ who played in last 30 days. Played == 'Started Level' analytics event. - .small Paid student: user.coursePrepaidID set and prepaid.properties.trialRequestID NOT set - .small Trial student: user.coursePrepaidID set and prepaid.properties.trialRequestID set - .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 + .tab-pane#tab_active_classes + h3 Active Classes 90 days + .small Active class: 12+ students in a classroom, with 6+ who played in last 30 days. Played == 'Started Level' analytics event. + .small Paid student: user.coursePrepaidID set and prepaid.properties.trialRequestID NOT set + .small Trial student: user.coursePrepaidID set and prepaid.properties.trialRequestID set + .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 - h3#recurring-revenue-graph Recurring Revenue 90 days - .recurring-revenue-chart.line-chart-container - - h3#classroom-daus-graph Classroom Daily Active Users 90 days - .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 - - h3#classroom-maus-graph Classroom Monthly Active Users 90 days - .classroom-monthly-active-users-chart.line-chart-container - - h3#campaign-daus-graph 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 - - h3#campaign-maus-graph Campaign Monthly Active Users 90 days - .campaign-monthly-active-users-chart.line-chart-container - - h3#campaign-vs-classroom-paid-maus-recent-graph Campaign vs Classroom Paid Monthly Active Users 90 days - .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 - - h3#enrollments-graph Enrollments Issued and Redeemed 90 days - .paid-courses-chart.line-chart-container - - h3#furthest-courses-table Furthest Course - .small Teacher: owner of a course instance - .small Student: member of a course instance (assigned to course) - .small For course instances created in last #{view.furthestCourseDayRange} days, not Single Player, hourOfCode != true - .small Counts are not summed. I.e. a student or teacher only contributes to the count of one course. - if view.teacherCourseDistribution - table.table.table-striped.table-condensed - tr - th Course - th Teachers - th Students - th Avg students per teacher - each count, courseIndex in view.teacherCourseDistribution + h1#active-classes-table Active Classes + table.table.table-striped.table-condensed tr - td= view.courses.models[courseIndex].get('name') - td= count - td= view.studentCourseDistribution[courseIndex] || 0 - td= Math.round((view.studentCourseDistribution[courseIndex] || 0) / count) - else - div Loading ... + th Day + for group in activeClassGroups + th= group.replace('Active classes', '') + each activeClass in activeClasses + tr + td= activeClass.day + each val in activeClass.groups + td= val - h3#school-counts-table School Counts - .small Only including schools with #{view.minSchoolCount}+ counts - if view.schoolCounts - table.table.table-striped.table-condensed - tr - th - th School Name - th User Count - each val, i in view.schoolCounts + + .tab-pane#tab_revenue + h3 Daily Recurring Revenue 90 days + .recurring-daily-revenue-chart-90.line-chart-container + + h3 Monthly Recurring Revenue 90 days + .recurring-monthly-revenue-chart-90.line-chart-container + + h3 Daily Recurring Revenue 365 days + .recurring-daily-revenue-chart-365.line-chart-container + + h3 Monthly Recurring Revenue 365 days + .recurring-monthly-revenue-chart-365.line-chart-container + + h1#recurring-revenue-table Recurring Revenue + table.table.table-striped.table-condensed tr - td= i + 1 - td= val.schoolName - td= val.count - else - div Loading ... + th Day + for group in revenueGroups + th= group.replace('DRR ', 'Daily ').replace('MRR ', 'Monthly ') + each entry in revenue + tr + td= entry.day + each val in entry.groups + td $#{(val / 100).toFixed(2)} - h1#active-classes-table Active Classes - table.table.table-striped.table-condensed - tr - th Day - for group in activeClassGroups - th= group.replace('Active classes', '') - each activeClass in activeClasses - tr - td= activeClass.day - each val in activeClass.groups - td= val + .tab-pane#tab_classroom + h3#classroom-daus-graph Classroom Daily Active Users 90 days + .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 - h1#recurring-revenue-table Recurring Revenue - table.table.table-striped.table-condensed - tr - th Day - for group in revenueGroups - th= group.replace('DRR ', 'Daily ') - each entry in revenue - tr - td= entry.day - each val in entry.groups - td $#{(val / 100).toFixed(2)} + h3#classroom-maus-graph Classroom Monthly Active Users 90 days + .classroom-monthly-active-users-chart.line-chart-container - h1#active-users-table Active Users - if activeUsers.length > 0 - - var eventNames = []; - each count, event in activeUsers[0].events - - eventNames.push(event) - - eventNames.sort(function (a, b) { - - if (a.indexOf('campaign') == b.indexOf('campaign') || a.indexOf('classroom') == b.indexOf('classroom')) { - - return a.localeCompare(b); - - } - - else if (a.indexOf('campaign') > b.indexOf('campaign')) { - - return 1; - - } - - else { - - return -1; - - } - - }); - table.table.table-striped.table-condensed - tr - th(style='min-width:85px;') Day - each eventName in eventNames - th= eventName - th DAU Campaign Total - th DAU Classroom Total - each activeUser in activeUsers - tr - td= activeUser.day - - var dauCampaignTotal = 0 - - var dauClassroomTotal = 0 - each eventName in eventNames - td= activeUser.events[eventName] || 0 - if eventName.indexOf('DAU campaign') >= 0 - - dauCampaignTotal += (activeUser.events[eventName] || 0); - else if eventName.indexOf('DAU classroom') >= 0 - - dauClassroomTotal += (activeUser.events[eventName] || 0); - td= dauCampaignTotal - td= dauClassroomTotal + h3#enrollments-graph Enrollments Issued and Redeemed 90 days + .paid-courses-chart.line-chart-container - h1#enrollments-table Enrollments - table.table.table-striped.table-condensed - tr - th Day - th Paid Enrollments Issued - th Paid Enrollments Redeemed - th Trial Enrollments Issued - th Trial Enrollments Redeemed - each day in enrollmentDays - tr - td= day - if dayEnrollmentsMap[day] - td= dayEnrollmentsMap[day].paidIssued || 0 - td= dayEnrollmentsMap[day].paidRedeemed || 0 - td= dayEnrollmentsMap[day].trialIssued || 0 - td= dayEnrollmentsMap[day].trialRedeemed || 0 + h3#furthest-courses-table Furthest Course + .small Teacher: owner of a course instance + .small Student: member of a course instance (assigned to course) + .small For course instances created in last #{view.furthestCourseDayRange} days, not Single Player, hourOfCode != true + .small Counts are not summed. I.e. a student or teacher only contributes to the count of one course. + if view.teacherCourseDistribution + table.table.table-striped.table-condensed + tr + th Course + th Teachers + th Students + th Avg students per teacher + each count, courseIndex in view.teacherCourseDistribution + tr + td= view.courses.models[courseIndex].get('name') + td= count + td= view.studentCourseDistribution[courseIndex] || 0 + td= Math.round((view.studentCourseDistribution[courseIndex] || 0) / count) + else + div Loading ... + + #school-counts + h3 School Counts + .small Only including schools with #{view.minSchoolCount}+ counts + if view.schoolCounts + table.table.table-striped.table-condensed + tr + th + th School Name + th User Count + each val, i in view.schoolCounts + tr + td= i + 1 + td= val.schoolName + td= val.count else - td 0 - td 0 - td 0 - td 0 + div Loading ... + + h1#active-users-table Active Users + if activeUsers.length > 0 + - var eventNames = []; + each count, event in activeUsers[0].events + if event.indexOf('classroom') >= 0 + - eventNames.push(event) + - eventNames.sort(function (a, b) {return a.localeCompare(b);}); + table.table.table-striped.table-condensed + tr + th(style='min-width:85px;') Day + each eventName in eventNames + th= eventName + th DAU Classroom Total + each activeUser in activeUsers + tr + td= activeUser.day + - var dauClassroomTotal = 0 + each eventName in eventNames + if eventName.indexOf('DAU') >= 0 + - dauClassroomTotal += (activeUser.events[eventName] || 0); + td= activeUser.events[eventName] || 0 + td= dauClassroomTotal + + h1#enrollments-table Enrollments + table.table.table-striped.table-condensed + tr + th Day + th Paid Enrollments Issued + th Paid Enrollments Redeemed + th Trial Enrollments Issued + th Trial Enrollments Redeemed + each day in enrollmentDays + tr + td= day + if dayEnrollmentsMap[day] + td= dayEnrollmentsMap[day].paidIssued || 0 + td= dayEnrollmentsMap[day].paidRedeemed || 0 + td= dayEnrollmentsMap[day].trialIssued || 0 + td= dayEnrollmentsMap[day].trialRedeemed || 0 + else + td 0 + td 0 + td 0 + td 0 + + .tab-pane#tab_campaign + h3#campaign-daus-graph 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 + + h3#campaign-maus-graph Campaign Monthly Active Users 90 days + .campaign-monthly-active-users-chart.line-chart-container + + h1#active-users-table Active Users + if activeUsers.length > 0 + - var eventNames = []; + each count, event in activeUsers[0].events + if event.indexOf('campaign') >= 0 + - eventNames.push(event) + - eventNames.sort(function (a, b) {return a.localeCompare(b);}); + table.table.table-striped.table-condensed + tr + th(style='min-width:85px;') Day + each eventName in eventNames + th= eventName + th DAU Total + each activeUser in activeUsers + tr + td= activeUser.day + - var dauCampaignTotal = 0 + each eventName in eventNames + if eventName.indexOf('DAU') >= 0 + - dauCampaignTotal += (activeUser.events[eventName] || 0); + td= activeUser.events[eventName] || 0 + td= dauCampaignTotal + + + .tab-pane#tab_campaign_vs_classroom + h3#campaign-vs-classroom-paid-maus-recent-graph Campaign vs Classroom Paid Monthly Active Users 90 days + .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 + if activeUsers.length > 0 + - var eventNames = []; + each count, event in activeUsers[0].events + - eventNames.push(event) + - eventNames.sort(function (a, b) { + - if (a.indexOf('campaign') == b.indexOf('campaign') || a.indexOf('classroom') == b.indexOf('classroom')) { + - return a.localeCompare(b); + - } + - else if (a.indexOf('campaign') > b.indexOf('campaign')) { + - return 1; + - } + - else { + - return -1; + - } + - }); + table.table.table-striped.table-condensed + tr + th(style='min-width:85px;') Day + each eventName in eventNames + th= eventName + th DAU Campaign Total + th DAU Classroom Total + each activeUser in activeUsers + tr + td= activeUser.day + - var dauCampaignTotal = 0 + - var dauClassroomTotal = 0 + each eventName in eventNames + td= activeUser.events[eventName] || 0 + if eventName.indexOf('DAU campaign') >= 0 + - dauCampaignTotal += (activeUser.events[eventName] || 0); + else if eventName.indexOf('DAU classroom') >= 0 + - dauClassroomTotal += (activeUser.events[eventName] || 0); + td= dauCampaignTotal + td= dauClassroomTotal diff --git a/app/views/admin/AnalyticsView.coffee b/app/views/admin/AnalyticsView.coffee index 9ca76f2f6..0ba3ba279 100644 --- a/app/views/admin/AnalyticsView.coffee +++ b/app/views/admin/AnalyticsView.coffee @@ -7,8 +7,6 @@ RootView = require 'views/core/RootView' template = require 'templates/admin/analytics' utils = require 'core/utils' -# TODO: switch page to tabs instead of table of contents - module.exports = class AnalyticsView extends RootView id: 'admin-analytics-view' template: template @@ -86,18 +84,20 @@ module.exports = class AnalyticsView extends RootView url: '/db/analytics_perday/-/recurring_revenue' method: 'POST' success: (data) => + # Organize data by day, then group groupMap = {} dayGroupCountMap = {} for dailyRevenue in data dayGroupCountMap[dailyRevenue.day] ?= {} - dayGroupCountMap[dailyRevenue.day]['Daily Total'] = 0 + dayGroupCountMap[dailyRevenue.day]['DRR Total'] = 0 for group, val of dailyRevenue.groups groupMap[group] = true dayGroupCountMap[dailyRevenue.day][group] = val - dayGroupCountMap[dailyRevenue.day]['Daily Total'] += val + dayGroupCountMap[dailyRevenue.day]['DRR Total'] += val @revenueGroups = Object.keys(groupMap) - @revenueGroups.push 'Daily Total' + @revenueGroups.push 'DRR Total' + # Build list of recurring revenue entries, where each entry is a day of individual group values @revenue = [] for day of dayGroupCountMap @@ -106,23 +106,35 @@ module.exports = class AnalyticsView extends RootView for group in @revenueGroups data.groups.push(dayGroupCountMap[day][group] ? 0) @revenue.push data + + # Order present to past @revenue.sort (a, b) -> b.day.localeCompare(a.day) return unless @revenue.length > 0 # Add monthly recurring revenue values - @revenueGroups.push 'Monthly' - monthlyValues = [] - for i in [@revenue.length-1..0] - dailyTotal = @revenue[i].groups[@revenue[i].groups.length - 1] - monthlyValues.push(dailyTotal) - monthlyValues.shift() while monthlyValues.length > 30 - if monthlyValues.length is 30 - @revenue[i].groups.push(_.reduce(monthlyValues, (s, num) -> s + num)) + + # For each daily group, add up monthly values walking forward through time, and add to revenue groups + monthlyDailyGroupMap = {} + dailyGroupIndexMap = {} + for group, i in @revenueGroups + monthlyDailyGroupMap[group.replace('DRR', 'MRR')] = group + dailyGroupIndexMap[group] = i + for monthlyGroup, dailyGroup of monthlyDailyGroupMap + monthlyValues = [] + for i in [@revenue.length-1..0] + dailyTotal = @revenue[i].groups[dailyGroupIndexMap[dailyGroup]] + monthlyValues.push(dailyTotal) + monthlyValues.shift() while monthlyValues.length > 30 + if monthlyValues.length is 30 + @revenue[i].groups.push(_.reduce(monthlyValues, (s, num) -> s + num)) + for monthlyGroup, dailyGroup of monthlyDailyGroupMap + @revenueGroups.push monthlyGroup @updateAllKPIChartData() @updateRevenueChartData() @render?() + }, 0).load() @supermodel.addRequestResource({ @@ -134,7 +146,7 @@ module.exports = class AnalyticsView extends RootView return -1 if a.count > b.count return 0 if a.count is b.count 1 - @render?() + @renderSelectors?('#school-counts') }, 0).load() @supermodel.addRequestResource({ @@ -272,17 +284,21 @@ module.exports = class AnalyticsView extends RootView points createLineCharts: -> - d3Utils.createLineChart('.kpi-recent-chart', @kpiRecentChartLines) - d3Utils.createLineChart('.kpi-chart', @kpiChartLines) - d3Utils.createLineChart('.active-classes-chart', @activeClassesChartLines) - d3Utils.createLineChart('.classroom-daily-active-users-chart', @classroomDailyActiveUsersChartLines) - d3Utils.createLineChart('.classroom-monthly-active-users-chart', @classroomMonthlyActiveUsersChartLines) - d3Utils.createLineChart('.campaign-daily-active-users-chart', @campaignDailyActiveUsersChartLines) - d3Utils.createLineChart('.campaign-monthly-active-users-chart', @campaignMonthlyActiveUsersChartLines) - d3Utils.createLineChart('.campaign-vs-classroom-monthly-active-users-recent-chart.line-chart-container', @campaignVsClassroomMonthlyActiveUsersRecentChartLines) - d3Utils.createLineChart('.campaign-vs-classroom-monthly-active-users-chart.line-chart-container', @campaignVsClassroomMonthlyActiveUsersChartLines) - d3Utils.createLineChart('.paid-courses-chart', @enrollmentsChartLines) - d3Utils.createLineChart('.recurring-revenue-chart', @revenueChartLines) + 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('.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) + d3Utils.createLineChart('.recurring-daily-revenue-chart-90', @revenueDailyChartLines90Days, visibleWidth) + d3Utils.createLineChart('.recurring-monthly-revenue-chart-90', @revenueMonthlyChartLines90Days, visibleWidth) + d3Utils.createLineChart('.recurring-daily-revenue-chart-365', @revenueDailyChartLines365Days, visibleWidth) + d3Utils.createLineChart('.recurring-monthly-revenue-chart-365', @revenueMonthlyChartLines365Days, visibleWidth) updateAllKPIChartData: -> @kpiRecentChartLines = [] @@ -644,9 +660,11 @@ module.exports = class AnalyticsView extends RootView line.max = dailyMax for line in @enrollmentsChartLines updateRevenueChartData: -> - @revenueChartLines = [] + @revenueDailyChartLines90Days = [] + @revenueMonthlyChartLines90Days = [] + @revenueDailyChartLines365Days = [] + @revenueMonthlyChartLines365Days = [] return unless @revenue?.length - days = d3Utils.createContiguousDays(90) groupDayMap = {} for entry in @revenue @@ -655,24 +673,31 @@ module.exports = class AnalyticsView extends RootView groupDayMap[@revenueGroups[i]][entry.day] ?= 0 groupDayMap[@revenueGroups[i]][entry.day] += count - colorIndex = 0 - dailyMax = 0 - for group, entries of groupDayMap - data = [] - for day, count of entries - data.push - day: day - value: count / 100 - data.reverse() - points = @createLineChartPoints(days, data) - @revenueChartLines.push - points: points - description: group.replace('DRR ', 'Daily ') - lineColor: @lineColors[colorIndex++ % @lineColors.length] - strokeWidth: 1 - min: 0 - max: _.max(points, 'y').y - showYScale: group in ['Daily Total', 'Monthly'] - dailyMax = _.max(points, 'y').y if group is 'Daily Total' - for line in @revenueChartLines when line.description isnt 'Monthly' - line.max = dailyMax + addRevenueChartLine = (days, eventPrefix, lines) => + colorIndex = 0 + dailyMax = 0 + for group, entries of groupDayMap + continue unless group.indexOf(eventPrefix) >= 0 + data = [] + for day, count of entries + data.push + day: day + value: count / 100 + data.reverse() + points = @createLineChartPoints(days, data) + lines.push + points: points + description: group.replace(eventPrefix + ' ', 'Daily ') + lineColor: @lineColors[colorIndex++ % @lineColors.length] + strokeWidth: 1 + min: 0 + max: _.max(points, 'y').y + showYScale: group is eventPrefix + ' Total' + dailyMax = _.max(points, 'y').y if group is eventPrefix + ' Total' + for line in lines + line.max = dailyMax + + addRevenueChartLine(d3Utils.createContiguousDays(90), 'DRR', @revenueDailyChartLines90Days) + addRevenueChartLine(d3Utils.createContiguousDays(90), 'MRR', @revenueMonthlyChartLines90Days) + addRevenueChartLine(d3Utils.createContiguousDays(365), 'DRR', @revenueDailyChartLines365Days) + addRevenueChartLine(d3Utils.createContiguousDays(365), 'MRR', @revenueMonthlyChartLines365Days)