Only return member sessions for assigned courses

Closes #3906
This commit is contained in:
Matt Lott 2016-09-03 11:18:35 -07:00
parent 346c29361b
commit b803080bed
4 changed files with 59 additions and 22 deletions

View file

@ -4,6 +4,7 @@ module.exports =
# Result: Each course instance gains a property, numCompleted, that is the
# number of students in that course instance who have completed ALL of
# the levels in thate course
# TODO: simplify, classroom.sessions only includes sessions for assigned courses now
calculateDots: (classrooms, courses, courseInstances) ->
for classroom in classrooms.models
# map [user, level] => session so we don't have to do find TODO

View file

@ -106,18 +106,34 @@ module.exports =
res.status(200).send(levels)
fetchMemberSessions: wrap (req, res, next) ->
# Return member sessions for assigned courses
throw new errors.Unauthorized() unless req.user
memberLimit = parse.getLimitFromReq(req, {default: 10, max: 100, param: 'memberLimit'})
memberSkip = parse.getSkipFromReq(req, {param: 'memberSkip'})
classroom = yield database.getDocFromHandle(req, Classroom)
throw new errors.NotFound('Classroom not found.') if not classroom
throw new errors.Forbidden('You do not own this classroom.') unless req.user.isAdmin() or classroom.get('ownerID').equals(req.user._id)
courseLevelsMap = {}
for course in classroom.get('courses') ? []
# TODO: is LevelSession.level.original really a string in practice, instead of ObjectId set in schema?
# https://github.com/codecombat/codecombat/blob/master/server/middleware/levels.coffee#L18
courseLevelsMap[course._id.toHexString()] = _.map(course.levels, (l) -> l.original?.toHexString())
courseInstances = yield CourseInstance.find({classroomID: classroom._id}).select('_id courseID members').lean()
memberCoursesMap = {}
for courseInstance in courseInstances
for userID in courseInstance.members ? []
memberCoursesMap[userID.toHexString()] ?= []
memberCoursesMap[userID.toHexString()].push(courseInstance.courseID)
memberLimit = parse.getLimitFromReq(req, {default: 10, max: 100, param: 'memberLimit'})
memberSkip = parse.getSkipFromReq(req, {param: 'memberSkip'})
members = classroom.get('members') or []
members = members.slice(memberSkip, memberSkip + memberLimit)
dbqs = []
select = 'state.complete level creator playtime changed created dateFirstCompleted submitted'
for member in members
dbqs.push(LevelSession.find({creator: member.toHexString()}).select(select).exec())
levelOriginals = []
for courseID in memberCoursesMap[member.toHexString()] ? []
levelOriginals = levelOriginals.concat(courseLevelsMap[courseID.toHexString()] ? [])
query = {creator: member.toHexString(), 'level.original': {$in: levelOriginals}}
dbqs.push(LevelSession.find(query).select(select).lean().exec())
results = yield dbqs
sessions = _.flatten(results)
res.status(200).send(sessions)

View file

@ -488,29 +488,36 @@ describe 'POST /db/classroom/:id/invite-members', ->
describe 'GET /db/classroom/:handle/member-sessions', ->
beforeEach utils.wrap (done) ->
yield utils.clearModels([User, Classroom, LevelSession, Level])
@artisan = yield utils.initUser()
@teacher = yield utils.initUser()
@student1 = yield utils.initUser()
@student2 = yield utils.initUser()
@levelA = new Level({name: 'Level A', permissions: [{target: @artisan._id, access: 'owner'}]})
@levelA.set('original', @levelA._id)
@levelA = yield @levelA.save()
@levelB = new Level({name: 'Level B', permissions: [{target: @artisan._id, access: 'owner'}]})
@levelB.set('original', @levelB._id)
@levelB = yield @levelB.save()
@classroom = yield new Classroom({name: 'Classroom', ownerID: @teacher._id, members: [@student1._id, @student2._id] }).save()
@session1A = yield new LevelSession({creator: @student1.id, state: { complete: true }, level: {original: @levelA._id}, permissions: [{target: @student1._id, access: 'owner'}]}).save()
@session1B = yield new LevelSession({creator: @student1.id, state: { complete: false }, level: {original: @levelB._id}, permissions: [{target: @student1._id, access: 'owner'}]}).save()
@session2A = yield new LevelSession({creator: @student2.id, state: { complete: true }, level: {original: @levelA._id}, permissions: [{target: @student2._id, access: 'owner'}]}).save()
@session2B = yield new LevelSession({creator: @student2.id, state: { complete: false }, level: {original: @levelB._id}, permissions: [{target: @student2._id, access: 'owner'}]}).save()
yield utils.clearModels([CourseInstance, Course, User, Classroom, Campaign, Level, LevelSession])
@teacher = yield utils.initUser({role: 'teacher'})
admin = yield utils.initAdmin()
yield utils.loginUser(admin)
@levelA = yield utils.makeLevel({type: 'course'})
@levelB = yield utils.makeLevel({type: 'course'})
@campaignA = yield utils.makeCampaign({}, {levels: [@levelA]})
@campaignB = yield utils.makeCampaign({}, {levels: [@levelB]})
@courseA = yield utils.makeCourse({free: true, releasePhase: 'released'}, {campaign: @campaignA})
@courseB = yield utils.makeCourse({free: true, releasePhase: 'released'}, {campaign: @campaignB})
@student1 = yield utils.initUser({role: 'student'})
yield utils.loginUser(@student1)
@session1A = yield utils.makeLevelSession({state: { complete: true }, level: {original: @levelA._id.toHexString()}})
@session1B = yield utils.makeLevelSession({state: { complete: false }, level: {original: @levelB._id.toHexString()}})
@student2 = yield utils.initUser({role: 'student'})
yield utils.loginUser(@student2)
@session2A = yield utils.makeLevelSession({state: { complete: true }, level: {original: @levelA._id.toHexString()}})
@session2B = yield utils.makeLevelSession({state: { complete: false }, level: {original: @levelB._id.toHexString()}})
yield utils.loginUser(@teacher)
@classroom = yield utils.makeClassroom({}, { members: [@student1, @student2] })
@courseInstanceA = yield utils.makeCourseInstance({courseID: @courseA.id, classroomID: @classroom.id}, { members: [@student1, @student2] })
@courseInstanceB = yield utils.makeCourseInstance({courseID: @courseB.id, classroomID: @classroom.id}, { members: [@student1] })
yield utils.logout()
done()
it 'returns all sessions for all members in the classroom with only properties level, creator and state.complete', utils.wrap (done) ->
it 'returns all sessions for all members in the classroom with assigned courses', utils.wrap (done) ->
yield utils.loginUser(@teacher)
[res, body] = yield request.getAsync getURL("/db/classroom/#{@classroom.id}/member-sessions"), { json: true }
expect(res.statusCode).toBe(200)
expect(body.length).toBe(4)
expect(body.length).toBe(3)
done()
it 'does not work if you are not the owner of the classroom', utils.wrap (done) ->
@ -532,7 +539,7 @@ describe 'GET /db/classroom/:handle/member-sessions', ->
expect(session.creator).toBe(@student1.id) for session in body
[res, body] = yield request.getAsync getURL("/db/classroom/#{@classroom.id}/member-sessions?memberSkip=1"), { json: true }
expect(res.statusCode).toBe(200)
expect(body.length).toBe(2)
expect(body.length).toBe(1)
expect(session.creator).toBe(@student2.id) for session in body
done()

View file

@ -209,6 +209,19 @@ module.exports = mw =
expect(res.statusCode).toBe(200)
ThangType.findById(res.body._id).exec done
makeLevelSession: Promise.promisify (data, sources, done) ->
args = Array.from(arguments)
[done, [data, sources]] = [args.pop(), args]
data = _.extend({}, {
creator: mw.lastLogin.id
permissions: [{target: mw.lastLogin.id, access: 'owner'}]
}, data)
# TODO: using request.post strips the creator field for some reason
session = new LevelSession data
session.save done
makeAchievement: Promise.promisify (data, sources, done) ->
args = Array.from(arguments)
[done, [data, sources]] = [args.pop(), args]