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