Add furthest course table to analytics page
This commit is contained in:
parent
729ebfec8f
commit
78c86eb979
3 changed files with 86 additions and 0 deletions
app
server/courses
|
@ -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
|
||||
|
|
|
@ -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 = []
|
||||
|
|
|
@ -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
|
||||
|
|
Reference in a new issue