2016-03-30 16:57:19 -04:00
|
|
|
errors = require '../commons/errors'
|
|
|
|
wrap = require 'co-express'
|
|
|
|
Promise = require 'bluebird'
|
|
|
|
database = require '../commons/database'
|
|
|
|
mongoose = require 'mongoose'
|
|
|
|
TrialRequest = require '../models/TrialRequest'
|
|
|
|
CourseInstance = require '../models/CourseInstance'
|
|
|
|
Classroom = require '../models/Classroom'
|
|
|
|
Course = require '../models/Course'
|
|
|
|
User = require '../models/User'
|
2016-04-13 12:54:24 -04:00
|
|
|
Level = require '../models/Level'
|
2016-06-26 16:51:14 -04:00
|
|
|
LevelSession = require '../models/LevelSession'
|
2016-04-13 12:54:24 -04:00
|
|
|
parse = require '../commons/parse'
|
2016-05-09 18:16:54 -04:00
|
|
|
{objectIdFromTimestamp} = require '../lib/utils'
|
2016-06-26 16:51:14 -04:00
|
|
|
utils = require '../../app/core/utils'
|
2016-05-09 18:16:54 -04:00
|
|
|
Prepaid = require '../models/Prepaid'
|
2016-03-30 16:57:19 -04:00
|
|
|
|
|
|
|
module.exports =
|
|
|
|
addMembers: wrap (req, res) ->
|
|
|
|
if req.body.userID
|
|
|
|
userIDs = [req.body.userID]
|
|
|
|
else if req.body.userIDs
|
|
|
|
userIDs = req.body.userIDs
|
|
|
|
else
|
|
|
|
throw new errors.UnprocessableEntity('Must provide userID or userIDs')
|
2016-06-26 16:51:14 -04:00
|
|
|
|
2016-03-30 16:57:19 -04:00
|
|
|
for userID in userIDs
|
|
|
|
unless _.all userIDs, database.isID
|
|
|
|
throw new errors.UnprocessableEntity('Invalid list of user IDs')
|
2016-06-26 16:51:14 -04:00
|
|
|
|
2016-03-30 16:57:19 -04:00
|
|
|
courseInstance = yield database.getDocFromHandle(req, CourseInstance)
|
|
|
|
if not courseInstance
|
|
|
|
throw new errors.NotFound('Course Instance not found.')
|
2016-06-26 16:51:14 -04:00
|
|
|
|
2016-03-30 16:57:19 -04:00
|
|
|
classroom = yield Classroom.findById courseInstance.get('classroomID')
|
|
|
|
if not classroom
|
|
|
|
throw new errors.NotFound('Classroom not found.')
|
2016-06-26 16:51:14 -04:00
|
|
|
|
2016-03-30 16:57:19 -04:00
|
|
|
classroomMembers = (userID.toString() for userID in classroom.get('members'))
|
|
|
|
unless _.all(userIDs, (userID) -> _.contains classroomMembers, userID)
|
|
|
|
throw new errors.Forbidden('Users must be members of classroom')
|
2016-06-26 16:51:14 -04:00
|
|
|
|
2016-03-30 19:20:37 -04:00
|
|
|
ownsClassroom = classroom.get('ownerID').equals(req.user._id)
|
|
|
|
addingSelf = userIDs.length is 1 and userIDs[0] is req.user.id
|
|
|
|
unless ownsClassroom or addingSelf
|
2016-03-30 16:57:19 -04:00
|
|
|
throw new errors.Forbidden('You must own the classroom to add members')
|
2016-06-26 16:51:14 -04:00
|
|
|
|
2016-03-30 16:57:19 -04:00
|
|
|
# Only the enrolled users
|
2016-05-09 18:16:54 -04:00
|
|
|
users = yield User.find({ _id: { $in: userIDs }}).select('coursePrepaid coursePrepaidID') # TODO: remove coursePrepaidID once migrated
|
|
|
|
usersAreEnrolled = _.all((user.isEnrolled() for user in users))
|
2016-06-26 16:51:14 -04:00
|
|
|
|
2016-03-30 16:57:19 -04:00
|
|
|
course = yield Course.findById courseInstance.get('courseID')
|
|
|
|
throw new errors.NotFound('Course referenced by course instance not found') unless course
|
2016-06-26 16:51:14 -04:00
|
|
|
|
2016-05-09 18:16:54 -04:00
|
|
|
if not (course.get('free') or usersAreEnrolled)
|
2016-03-30 16:57:19 -04:00
|
|
|
throw new errors.PaymentRequired('Cannot add users to a course instance until they are added to a prepaid')
|
2016-06-26 16:51:14 -04:00
|
|
|
|
2016-03-30 16:57:19 -04:00
|
|
|
userObjectIDs = (mongoose.Types.ObjectId(userID) for userID in userIDs)
|
2016-06-26 16:51:14 -04:00
|
|
|
|
2016-03-30 16:57:19 -04:00
|
|
|
courseInstance = yield CourseInstance.findByIdAndUpdate(
|
|
|
|
courseInstance._id,
|
|
|
|
{ $addToSet: { members: { $each: userObjectIDs } } }
|
|
|
|
{ new: true }
|
|
|
|
)
|
2016-06-26 16:51:14 -04:00
|
|
|
|
2016-03-30 16:57:19 -04:00
|
|
|
userUpdateResult = yield User.update(
|
|
|
|
{ _id: { $in: userObjectIDs } },
|
|
|
|
{ $addToSet: { courseInstances: courseInstance._id } }
|
|
|
|
)
|
2016-06-26 16:51:14 -04:00
|
|
|
|
2016-03-30 16:57:19 -04:00
|
|
|
res.status(200).send(courseInstance.toObject({ req }))
|
2016-04-13 12:54:24 -04:00
|
|
|
|
|
|
|
|
|
|
|
fetchNextLevel: wrap (req, res) ->
|
2016-06-28 12:44:58 -04:00
|
|
|
unless req.user? then return res.status(200).send({})
|
2016-04-13 12:54:24 -04:00
|
|
|
levelOriginal = req.params.levelOriginal
|
2016-06-26 16:51:14 -04:00
|
|
|
unless database.isID(levelOriginal) then throw new errors.UnprocessableEntity('Invalid level original ObjectId')
|
|
|
|
sessionID = req.params.sessionID
|
|
|
|
unless database.isID(sessionID) then throw new errors.UnprocessableEntity('Invalid session ObjectId')
|
2016-04-13 12:54:24 -04:00
|
|
|
courseInstance = yield database.getDocFromHandle(req, CourseInstance)
|
2016-06-26 16:51:14 -04:00
|
|
|
unless courseInstance then throw new errors.NotFound('Course Instance not found.')
|
|
|
|
classroom = yield Classroom.findById courseInstance.get('classroomID')
|
|
|
|
unless classroom then throw new errors.NotFound('Classroom not found.')
|
|
|
|
currentLevel = yield Level.findOne({original: mongoose.Types.ObjectId(levelOriginal)}, {practiceThresholdMinutes: 1, type: 1})
|
|
|
|
unless currentLevel then throw new errors.NotFound('Current level not found.')
|
|
|
|
|
2016-04-13 12:54:24 -04:00
|
|
|
courseID = courseInstance.get('courseID')
|
2016-06-26 16:51:14 -04:00
|
|
|
courseLevels = []
|
|
|
|
courseLevels = course.levels for course in classroom.get('courses') or [] when courseID.equals(course._id)
|
2016-04-13 12:54:24 -04:00
|
|
|
|
2016-06-26 16:51:14 -04:00
|
|
|
# Get level completions and playtime
|
|
|
|
currentLevelSession = null
|
|
|
|
levelIDs = (level.original.toString() for level in courseLevels)
|
|
|
|
query = {$and: [{creator: req.user.id}, {'level.original': {$in: levelIDs}}]}
|
|
|
|
levelSessions = yield LevelSession.find(query, {level: 1, playtime: 1, state: 1})
|
|
|
|
levelCompleteMap = {}
|
|
|
|
for levelSession in levelSessions
|
|
|
|
currentLevelSession = levelSession if levelSession.id is sessionID
|
|
|
|
levelCompleteMap[levelSession.get('level')?.original] = levelSession.get('state')?.complete
|
|
|
|
unless currentLevelSession then throw new errors.NotFound('Level session not found.')
|
|
|
|
needsPractice = utils.needsPractice(currentLevelSession.get('playtime'), currentLevel.get('practiceThresholdMinutes'))
|
|
|
|
|
|
|
|
# Find next level
|
|
|
|
levels = []
|
|
|
|
currentIndex = -1
|
|
|
|
for level, index in courseLevels
|
|
|
|
currentIndex = index if level.original.toString() is levelOriginal
|
|
|
|
levels.push
|
|
|
|
practice: level.practice ? false
|
|
|
|
complete: levelCompleteMap[level.original?.toString()] or currentIndex is index
|
|
|
|
unless currentIndex >=0 then throw new errors.NotFound('Level original ObjectId not found in Classroom courses')
|
|
|
|
nextLevelIndex = utils.findNextLevel(levels, currentIndex, needsPractice)
|
|
|
|
nextLevelOriginal = courseLevels[nextLevelIndex]?.original
|
|
|
|
unless nextLevelOriginal then return res.status(200).send({})
|
|
|
|
|
|
|
|
# Return full Level object
|
2016-04-13 12:54:24 -04:00
|
|
|
dbq = Level.findOne({original: mongoose.Types.ObjectId(nextLevelOriginal)})
|
|
|
|
dbq.sort({ 'version.major': -1, 'version.minor': -1 })
|
|
|
|
dbq.select(parse.getProjectFromReq(req))
|
|
|
|
level = yield dbq
|
|
|
|
level = level.toObject({req: req})
|
|
|
|
res.status(200).send(level)
|
2016-06-26 16:51:14 -04:00
|
|
|
|
2016-04-13 12:54:24 -04:00
|
|
|
fetchClassroom: wrap (req, res) ->
|
|
|
|
courseInstance = yield database.getDocFromHandle(req, CourseInstance)
|
|
|
|
if not courseInstance
|
|
|
|
throw new errors.NotFound('Course Instance not found.')
|
|
|
|
|
|
|
|
classroom = yield Classroom.findById(courseInstance.get('classroomID')).select(parse.getProjectFromReq(req))
|
|
|
|
if not classroom
|
|
|
|
throw new errors.NotFound('Classroom not found.')
|
|
|
|
|
|
|
|
isOwner = classroom.get('ownerID')?.equals req.user?._id
|
|
|
|
isMember = _.any(classroom.get('members') or [], (memberID) -> memberID.equals(req.user.get('_id')))
|
|
|
|
if not (isOwner or isMember)
|
|
|
|
throw new errors.Forbidden('You do not have access to this classroom')
|
|
|
|
|
|
|
|
classroom = classroom.toObject({req: req})
|
|
|
|
|
2016-04-27 18:36:16 -04:00
|
|
|
res.status(200).send(classroom)
|
2016-05-09 18:16:54 -04:00
|
|
|
|
2016-06-26 16:51:14 -04:00
|
|
|
|
2016-07-15 18:54:22 -04:00
|
|
|
fetchCourse: wrap (req, res) ->
|
|
|
|
courseInstance = yield database.getDocFromHandle(req, CourseInstance)
|
|
|
|
if not courseInstance
|
|
|
|
throw new errors.NotFound('Course Instance not found.')
|
|
|
|
|
|
|
|
course = yield Course.findById(courseInstance.get('courseID')).select(parse.getProjectFromReq(req))
|
|
|
|
if not course
|
|
|
|
throw new errors.NotFound('Course not found.')
|
|
|
|
|
|
|
|
res.status(200).send(course.toObject({req: req}))
|
|
|
|
|
|
|
|
|
2016-05-09 18:16:54 -04:00
|
|
|
fetchRecent: wrap (req, res) ->
|
|
|
|
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?
|
|
|
|
courseInstances = yield CourseInstance.find(query, {courseID: 1, members: 1, ownerID: 1})
|
2016-06-26 16:51:14 -04:00
|
|
|
|
2016-05-09 18:16:54 -04:00
|
|
|
userIDs = []
|
|
|
|
for courseInstance in courseInstances
|
|
|
|
if members = courseInstance.get('members')
|
|
|
|
userIDs.push(userID) for userID in members
|
2016-05-23 13:26:34 -04:00
|
|
|
users = yield User.find({_id: {$in: userIDs}}, {coursePrepaid: 1, coursePrepaidID: 1})
|
2016-06-26 16:51:14 -04:00
|
|
|
|
2016-05-09 18:16:54 -04:00
|
|
|
prepaidIDs = []
|
|
|
|
for user in users
|
|
|
|
if prepaidID = user.get('coursePrepaid')
|
|
|
|
prepaidIDs.push(prepaidID._id)
|
|
|
|
prepaids = yield Prepaid.find({_id: {$in: prepaidIDs}}, {properties: 1})
|
2016-06-26 16:51:14 -04:00
|
|
|
|
2016-05-09 18:16:54 -04:00
|
|
|
res.send({
|
|
|
|
courseInstances: (courseInstance.toObject({req: req}) for courseInstance in courseInstances)
|
|
|
|
students: (user.toObject({req: req}) for user in users)
|
|
|
|
prepaids: (prepaid.toObject({req: req}) for prepaid in prepaids)
|
|
|
|
})
|
2016-06-28 19:41:33 -04:00
|
|
|
|
|
|
|
fetchNonHoc: wrap (req, res) ->
|
|
|
|
throw new errors.Unauthorized('You must be an administrator.') unless req.user?.isAdmin()
|
|
|
|
query = {$and: [{name: {$ne: 'Single Player'}}, {hourOfCode: {$ne: true}}]}
|
|
|
|
courseInstances = yield CourseInstance.find(query, { members: 1, ownerID: 1}).lean()
|
|
|
|
res.status(200).send(courseInstances)
|