From 019406a341a516cd556bb23c337bbafbae3d1e5c Mon Sep 17 00:00:00 2001 From: Matt Lott <mattlott@live.com> Date: Mon, 26 Jan 2015 14:58:35 -0800 Subject: [PATCH] Update campaign analytics Increase line graph dots for larger hover targets. Update missing day data handling to fill in graph points for any missing day, not just most recent end days. Fix level completion div0 bug. --- .../editor/campaign/CampaignLevelView.coffee | 100 +++++++++--------- server/levels/level_handler.coffee | 2 + 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/app/views/editor/campaign/CampaignLevelView.coffee b/app/views/editor/campaign/CampaignLevelView.coffee index 235a7a31e..96358c032 100644 --- a/app/views/editor/campaign/CampaignLevelView.coffee +++ b/app/views/editor/campaign/CampaignLevelView.coffee @@ -103,12 +103,22 @@ module.exports = class CampaignLevelView extends CocoView description: 'Help video rate (%)' color: 'purple' - # Last day may be missing due to caching, will use this days aggregate to clean up individual graph lines + # Use this days aggregate to fill in missing days from the analytics data days = {} - days[day.created] = true for day in @analytics.levelCompletions.data if @analytics?.levelCompletions?.data? - days[day.created.replace(/-/g, '')] = true for day in @analytics.levelPlaytimes.data if @analytics?.levelPlaytimes?.data? - days[day.day] = true for day in @analytics.levelHelps.data if @analytics?.levelHelps?.data? + days["#{day.created[0..3]}-#{day.created[4..5]}-#{day.created[6..7]}"] = true for day in @analytics.levelCompletions.data if @analytics?.levelCompletions?.data? + days[day.created] = true for day in @analytics.levelPlaytimes.data if @analytics?.levelPlaytimes?.data? + days["#{day.day[0..3]}-#{day.day[4..5]}-#{day.day[6..7]}"] = true for day in @analytics.levelHelps.data if @analytics?.levelHelps?.data? days = Object.keys(days).sort (a, b) -> if a < b then -1 else 1 + if days.length > 0 + currentIndex = 0 + currentDay = days[currentIndex] + currentDate = new Date(currentDay + "T00:00:00.000Z") + lastDay = days[days.length - 1] + while currentDay isnt lastDay + days.splice currentIndex, 0, currentDay if days[currentIndex] isnt currentDay + currentIndex++ + currentDate.setUTCDate(currentDate.getUTCDate() + 1) + currentDay = currentDate.toISOString().substr(0, 10) # Update level completion graph data dayStartedMap = {} @@ -126,17 +136,14 @@ module.exports = class CampaignLevelView extends CocoView pointID: "#{completionLineID}#{i}" values: ["Started: #{day.started}", "Finished: #{day.finished}", "Completion rate: #{rate.toFixed(2)}%"] # Ensure points for each day - if levelPoints.length < days.length - for i in [1..days.length - levelPoints.length] - day = days[days.length - i] - x = levelPoints[levelPoints.length - 1].x + 1 - levelPoints.push - x: x + for day, i in days + if levelPoints.length <= i or levelPoints[i].day isnt day + levelPoints.splice i, 0, y: 0.0 - started: 0 - day: "#{day[0..3]}-#{day[4..5]}-#{day[6..7]}" - pointID: "#{completionLineID}#{x}" + day: day values: [] + levelPoints[i].x = i + levelPoints[i].pointID = "#{completionLineID}#{i}" @analytics.graphs[0].lines.push lineID: completionLineID enabled: true @@ -159,17 +166,14 @@ module.exports = class CampaignLevelView extends CocoView pointID: "#{playtimeLineID}#{i}" values: ["Average playtime: #{avg.toFixed(2)}s"] # Ensure points for each day - if playtimePoints.length < days.length - for i in [1..days.length - playtimePoints.length] - day = days[days.length - i] - x = playtimePoints[playtimePoints.length - 1].x + 1 - playtimePoints.push - x: x + for day, i in days + if playtimePoints.length <= i or playtimePoints[i].day isnt day + playtimePoints.splice i, 0, y: 0.0 - started: 0 - day: "#{day[0..3]}-#{day[4..5]}-#{day[6..7]}" - pointID: "#{completionLineID}#{x}" + day: day values: [] + playtimePoints[i].x = i + playtimePoints[i].pointID = "#{playtimeLineID}#{i}" @analytics.graphs[0].lines.push lineID: playtimeLineID enabled: true @@ -187,7 +191,7 @@ module.exports = class CampaignLevelView extends CocoView for day, i in @analytics.levelHelps.data helpCount = day.alertHelps + day.paletteHelps started = dayStartedMap[day.day] ? 0 - clickRate = if started > 0 then helpCount / started * 100 else -1.0 + clickRate = if started > 0 then helpCount / started * 100 else 0 videoRate = day.videoStarts / helpCount * 100 helpPoints.push x: i @@ -202,36 +206,30 @@ module.exports = class CampaignLevelView extends CocoView pointID: "#{videosLineID}#{i}" values: ["Help videos started: #{day.videoStarts}", "Help videos start rate: #{videoRate.toFixed(2)}%"] # Ensure points for each day - if helpPoints.length < days.length - for i in [1..days.length - helpPoints.length] - day = days[days.length - i] - x = helpPoints[helpPoints.length - 1].x + 1 - helpPoints.push - x: x + for day, i in days + if helpPoints.length <= i or helpPoints[i].day isnt day + helpPoints.splice i, 0, y: 0.0 - started: 0 - day: "#{day[0..3]}-#{day[4..5]}-#{day[6..7]}" - pointID: "#{helpsLineID}#{x}" + day: day values: [] - if videoPoints.length < days.length - for i in [1..days.length - videoPoints.length] - day = days[days.length - i] - x = videoPoints[videoPoints.length - 1].x + 1 - helpPoints.push - x: x + helpPoints[i].x = i + helpPoints[i].pointID = "#{helpsLineID}#{i}" + if videoPoints.length <= i or videoPoints[i].day isnt day + videoPoints.splice i, 0, y: 0.0 - started: 0 - day: "#{day[0..3]}-#{day[4..5]}-#{day[6..7]}" - pointID: "#{videosLineID}#{x}" + day: day values: [] - @analytics.graphs[0].lines.push - lineID: helpsLineID - enabled: true - points: helpPoints - description: lineMetadata[helpsLineID].description - lineColor: lineMetadata[helpsLineID].color - min: 0 - max: 100.0 + videoPoints[i].x = i + videoPoints[i].pointID = "#{videosLineID}#{i}" + if d3.max(helpPoints, (d) -> d.y) > 0 + @analytics.graphs[0].lines.push + lineID: helpsLineID + enabled: true + points: helpPoints + description: lineMetadata[helpsLineID].description + lineColor: lineMetadata[helpsLineID].color + min: 0 + max: 100.0 if d3.max(videoPoints, (d) -> d.y) > 0 @analytics.graphs[0].lines.push lineID: videosLineID @@ -336,7 +334,7 @@ module.exports = class CampaignLevelView extends CocoView .attr("transform", "translate(" + (margin + yAxisWidth * graphLineCount) + "," + margin + ")") .attr("cx", (d) -> xRange(d.x)) .attr("cy", (d) -> yRange(d.y)) - .attr("r", (d) -> if d.started then Math.max(3, Math.min(10, Math.log(parseInt(d.started)))) else 4) + .attr("r", (d) -> if d.started then Math.max(3, Math.min(10, Math.log(parseInt(d.started)))) + 2 else 6) .attr("fill", line.lineColor) .attr("stroke-width", 1) .attr("class", "graph-point") @@ -412,7 +410,7 @@ module.exports = class CampaignLevelView extends CocoView # console.log 'getLevelCompletions', data data.sort (a, b) -> if a.created < b.created then -1 else 1 mapFn = (item) -> - item.rate = item.finished / item.started * 100 + item.rate = if item.started > 0 then item.finished / item.started * 100 else 0 item @analytics.levelCompletions.data = _.map data, mapFn, @ doneCallback() diff --git a/server/levels/level_handler.coffee b/server/levels/level_handler.coffee index 1831b5b35..aa6adeacb 100644 --- a/server/levels/level_handler.coffee +++ b/server/levels/level_handler.coffee @@ -358,6 +358,8 @@ LevelHandler = class LevelHandler extends Handler # TODO: An uncached call takes about 5s for dungeons-of-kithgard locally # TODO: This is very similar to getLevelCompletionsBySlugs(), time to generalize analytics APIs? + # TODO: exclude admin data + levelSlugs = req.query.slugs or req.body.slugs startDay = req.query.startDay or req.body.startDay endDay = req.query.endDay or req.body.endDay