codecombat/server/middleware/levels.coffee
Matt Lott d72e4eb750 Practice levels Ux and next level algorithm
Update classroom and gameplay Ux to surface practice levels as 3a, 3b,
etc.
Update next level logic to leverage practice levels based on per level
completion playtime thresholds.
Patrol buster and patrol buster A are live for testing.
Fix a few classroom Ux progress hover bubble info bugs.

Closes #3767
2016-06-27 14:05:42 -07:00

85 lines
3.5 KiB
CoffeeScript

wrap = require 'co-express'
errors = require '../commons/errors'
Level = require '../models/Level'
LevelSession = require '../models/LevelSession'
CourseInstance = require '../models/CourseInstance'
Classroom = require '../models/Classroom'
Course = require '../models/Course'
database = require '../commons/database'
module.exports =
upsertSession: wrap (req, res) ->
level = yield database.getDocFromHandle(req, Level)
if not level
throw new errors.NotFound('Level not found.')
sessionQuery =
level:
original: level.get('original').toString()
majorVersion: level.get('version').major
creator: req.user.id
if req.query.team?
sessionQuery.team = req.query.team
session = yield LevelSession.findOne(sessionQuery)
if session
return res.send(session.toObject({req: req}))
attrs = sessionQuery
_.extend(attrs, {
state:
complete: false
scripts:
currentScript: null # will not save empty objects
permissions: [
{target: req.user.id, access: 'owner'}
{target: 'public', access: 'write'}
]
codeLanguage: req.user.get('aceConfig')?.language ? 'python'
})
if level.get('type') in ['course', 'course-ladder'] or req.query.course?
# Find the course and classroom that has assigned this level, verify access
courseInstances = yield CourseInstance.find({members: req.user._id})
classroomIDs = (courseInstance.get('classroomID') for courseInstance in courseInstances)
classroomIDs = _.filter _.uniq classroomIDs, false, (objectID='') -> objectID.toString()
classrooms = yield Classroom.find({ _id: { $in: classroomIDs }})
classroomWithLevel = null
courseID = null
classroomMap = {}
classroomMap[classroom.id] = classroom for classroom in classrooms
levelOriginal = level.get('original')
for courseInstance in courseInstances
classroomID = courseInstance.get('classroomID')
continue unless classroomID
classroom = classroomMap[classroomID.toString()]
continue unless classroom
courseID = courseInstance.get('courseID')
classroomCourse = _.find(classroom.get('courses'), (c) -> c._id.equals(courseID))
for courseLevel in classroomCourse.levels
if courseLevel.original.equals(levelOriginal)
classroomWithLevel = classroom
break
break if classroomWithLevel
unless classroomWithLevel
throw new errors.PaymentRequired('You must be in a course which includes this level to play it')
course = yield Course.findById(courseID).select('free')
unless course.get('free') or req.user.isEnrolled()
throw new errors.PaymentRequired('You must be enrolled to access this content')
lang = classroomWithLevel.get('aceConfig')?.language
attrs.codeLanguage = lang if lang
else
requiresSubscription = level.get('requiresSubscription') or (req.user.isOnPremiumServer() and level.get('campaign') and not (level.slug in ['dungeons-of-kithgard', 'gems-in-the-deep', 'shadow-guard', 'forgetful-gemsmith', 'signs-and-portents', 'true-names']))
canPlayAnyway = req.user.isPremium() or level.get 'adventurer'
if requiresSubscription and not canPlayAnyway
throw new errors.PaymentRequired('This level requires a subscription to play')
session = new LevelSession(attrs)
yield session.save()
res.send(session.toObject({req: req}))