Add furthest course table to analytics page

This commit is contained in:
Matt Lott 2016-01-26 14:36:40 -08:00
parent 729ebfec8f
commit 78c86eb979
3 changed files with 86 additions and 0 deletions
app
templates/admin
views/admin
server/courses

View file

@ -35,6 +35,27 @@ block content
h3 Active Users 90 days
.active-users-chart.line-chart-container
h3 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 ...
h1 Active Classes
table.table.table-striped.table-condensed
tr

View file

@ -1,3 +1,6 @@
CocoCollection = require 'collections/CocoCollection'
Course = require 'models/Course'
CourseInstance = require 'models/CourseInstance'
require 'vendor/d3'
d3Utils = require 'core/d3_utils'
RootView = require 'views/core/RootView'
@ -7,6 +10,7 @@ utils = require 'core/utils'
module.exports = class AnalyticsView extends RootView
id: 'admin-analytics-view'
template: template
furthestCourseDayRange: 30
lineColors: ['red', 'blue', 'green', 'purple', 'goldenrod', 'brown', 'darkcyan']
constructor: (options) ->
@ -114,6 +118,56 @@ module.exports = class AnalyticsView extends RootView
@updateRevenueChartData()
@render?()
}, 0).load()
@courses = new CocoCollection([], { url: "/db/course", model: Course})
@courses.comparator = "_id"
@listenToOnce @courses, 'sync', @onCoursesSync
@supermodel.loadCollection(@courses, 'courses')
onCoursesSync: ->
# Assumes courses retrieved in order
@courseOrderMap = {}
@courseOrderMap[@courses.models[i].get('_id')] = i for i in [0...@courses.models.length]
startDay = new Date()
startDay.setUTCDate(startDay.getUTCDate() - @furthestCourseDayRange)
startDay = startDay.toISOString().substring(0, 10)
options =
url: '/db/course_instance/-/recent'
method: 'POST'
data: {startDay: startDay}
options.error = (models, response, options) =>
return if @destroyed
console.error 'Failed to get recent course instances', response
options.success = (models) =>
@courseInstances = models ? []
@onCourseInstancesSync()
@render?()
@supermodel.addRequestResource('get_recent_course_instances', options, 0).load()
onCourseInstancesSync: ->
return unless @courseInstances
# Find highest course for teachers and students
@teacherFurthestCourseMap = {}
@studentFurthestCourseMap = {}
for courseInstance in @courseInstances
courseID = courseInstance.courseID
teacherID = courseInstance.ownerID
if not @teacherFurthestCourseMap[teacherID] or @teacherFurthestCourseMap[teacherID] < @courseOrderMap[courseID]
@teacherFurthestCourseMap[teacherID] = @courseOrderMap[courseID]
for studentID in courseInstance.members
if not @studentFurthestCourseMap[studentID] or @studentFurthestCourseMap[studentID] < @courseOrderMap[courseID]
@studentFurthestCourseMap[studentID] = @courseOrderMap[courseID]
@teacherCourseDistribution = {}
for teacherID, courseIndex of @teacherFurthestCourseMap
@teacherCourseDistribution[courseIndex] ?= 0
@teacherCourseDistribution[courseIndex]++
@studentCourseDistribution = {}
for studentID, courseIndex of @studentFurthestCourseMap
@studentCourseDistribution[courseIndex] ?= 0
@studentCourseDistribution[courseIndex]++
createLineChartPoints: (days, data) ->
points = []

View file

@ -11,6 +11,7 @@ PrepaidHandler = require '../prepaids/prepaid_handler'
User = require '../users/User'
UserHandler = require '../users/user_handler'
utils = require '../../app/core/utils'
{objectIdFromTimestamp} = require '../lib/utils'
sendwithus = require '../sendwithus'
mongoose = require 'mongoose'
@ -38,6 +39,7 @@ CourseInstanceHandler = class CourseInstanceHandler extends Handler
return @removeMember(req, res, args[0]) if req.method is 'DELETE' and args[1] is 'members'
return @getMembersAPI(req, res, args[0]) if args[1] is 'members'
return @inviteStudents(req, res, args[0]) if relationship is 'invite_students'
return @getRecentAPI(req, res) if relationship is 'recent'
return @redeemPrepaidCodeAPI(req, res) if args[1] is 'redeem_prepaid'
return @getMyCourseLevelSessionsAPI(req, res, args[0]) if args[1] is 'my-course-level-sessions'
return @findByLevel(req, res, args[2]) if args[1] is 'find_by_level'
@ -193,6 +195,15 @@ CourseInstanceHandler = class CourseInstanceHandler extends Handler
cleandocs = (UserHandler.formatEntity(req, doc) for doc in users)
@sendSuccess(res, cleandocs)
getRecentAPI: (req, res) ->
return @sendUnauthorizedError(res) unless req.user?.isAdmin()
query = {$and: [{name: {$ne: 'Single Player'}}, {hourOfCode: {$ne: true}}]}
query["$and"].push(_id: {$gte: objectIdFromTimestamp(req.body.startDay + "T00:00:00.000Z")}) if req.body.startDay?
query["$and"].push(_id: {$lt: objectIdFromTimestamp(req.body.endDay + "T00:00:00.000Z")}) if req.body.endDay?
CourseInstance.find query, (err, courseInstances) =>
return @sendDatabaseError(res, err) if err
@sendSuccess(res, courseInstances)
inviteStudents: (req, res, courseInstanceID) ->
return @sendUnauthorizedError(res) if not req.user?
if not req.body.emails