Add primer level support to classroom Ux
Exclude levels if classroom.aceConfig.language == level.primerLanguage Closes #3856
This commit is contained in:
parent
efa4b2b158
commit
84e3ee270a
12 changed files with 429 additions and 231 deletions
app
models
templates/courses
views
server/middleware
spec/server/functional
test/app
|
@ -47,9 +47,11 @@ module.exports = class Classroom extends CocoModel
|
||||||
getLevelNumber: (levelID, defaultNumber) ->
|
getLevelNumber: (levelID, defaultNumber) ->
|
||||||
unless @levelNumberMap
|
unless @levelNumberMap
|
||||||
@levelNumberMap = {}
|
@levelNumberMap = {}
|
||||||
|
language = @get('aceConfig')?.language
|
||||||
for course in @get('courses') ? []
|
for course in @get('courses') ? []
|
||||||
levels = []
|
levels = []
|
||||||
for level in course.levels when level.original
|
for level in course.levels when level.original
|
||||||
|
continue if language? and level.primerLanguage is language
|
||||||
levels.push({key: level.original, practice: level.practice ? false})
|
levels.push({key: level.original, practice: level.practice ? false})
|
||||||
_.assign(@levelNumberMap, utils.createLevelNumberMap(levels))
|
_.assign(@levelNumberMap, utils.createLevelNumberMap(levels))
|
||||||
@levelNumberMap[levelID] ? defaultNumber
|
@levelNumberMap[levelID] ? defaultNumber
|
||||||
|
@ -84,6 +86,8 @@ module.exports = class Classroom extends CocoModel
|
||||||
continue
|
continue
|
||||||
levelObjects.push(course.levels)
|
levelObjects.push(course.levels)
|
||||||
levels = new Levels(_.flatten(levelObjects))
|
levels = new Levels(_.flatten(levelObjects))
|
||||||
|
language = @get('aceConfig')?.language
|
||||||
|
levels.remove(levels.filter((level) => level.get('primerLanguage') is language)) if language
|
||||||
if options.withoutLadderLevels
|
if options.withoutLadderLevels
|
||||||
levels.remove(levels.filter((level) -> level.isLadder()))
|
levels.remove(levels.filter((level) -> level.isLadder()))
|
||||||
if options.projectLevels
|
if options.projectLevels
|
||||||
|
|
|
@ -37,7 +37,15 @@ block content
|
||||||
span(data-i18n="courses.select_language")
|
span(data-i18n="courses.select_language")
|
||||||
| :
|
| :
|
||||||
select.language-select.form-control
|
select.language-select.form-control
|
||||||
// TODO: Automate this list @scott
|
//- TODO: Automate this list @scott
|
||||||
|
//- Web dev courses use HTML and JavaScript, except web-dev-1 which doesn't have scripting
|
||||||
|
if course.get('slug') === 'web-dev-1'
|
||||||
|
option(value="javascript")
|
||||||
|
| HTML
|
||||||
|
else if course.get('slug').indexOf('web-dev') >= 0
|
||||||
|
option(value="javascript")
|
||||||
|
| HTML / JavaScript
|
||||||
|
else
|
||||||
option(value="python")
|
option(value="python")
|
||||||
| Python
|
| Python
|
||||||
option(value="javascript")
|
option(value="javascript")
|
||||||
|
@ -86,11 +94,19 @@ mixin course-info(course)
|
||||||
span.spr ,
|
span.spr ,
|
||||||
|
|
||||||
if me.isTeacher() || view.ownedClassrooms.size() || me.isAdmin()
|
if me.isTeacher() || view.ownedClassrooms.size() || me.isAdmin()
|
||||||
p
|
//- Web dev courses use HTML and JavaScript, except web-dev-1 which doesn't have scripting
|
||||||
a.guide-btn.btn.btn-primary(href=("/teachers/course-solution/" + course.id + "/python") data-course-id=course.id data-course-name=course.get('name') data-event-action="Classes Guides Guide Python" class=(me.isTeacher() || me.isAdmin() ? '': 'disabled'))
|
if course.get('slug') === 'web-dev-1'
|
||||||
|
a.guide-btn.btn.btn-primary(href=("/teachers/course-solution/" + course.id + "/javascript") data-course-id=course.id data-course-name=course.get('name') data-event-action="Classes Guides Guide JavaScript" class=(me.isTeacher() || me.isAdmin() ? '': 'disabled'))
|
||||||
span(data-i18n="courses.view_guide_online")
|
span(data-i18n="courses.view_guide_online")
|
||||||
| — Python
|
| — HTML
|
||||||
p
|
else if course.get('slug').indexOf('web-dev') >= 0
|
||||||
|
a.guide-btn.btn.btn-primary(href=("/teachers/course-solution/" + course.id + "/javascript") data-course-id=course.id data-course-name=course.get('name') data-event-action="Classes Guides Guide JavaScript" class=(me.isTeacher() || me.isAdmin() ? '': 'disabled'))
|
||||||
|
span(data-i18n="courses.view_guide_online")
|
||||||
|
| — HTML / JavaScript
|
||||||
|
else
|
||||||
a.guide-btn.btn.btn-primary(href=("/teachers/course-solution/" + course.id + "/javascript") data-course-id=course.id data-course-name=course.get('name') data-event-action="Classes Guides Guide JavaScript" class=(me.isTeacher() || me.isAdmin() ? '': 'disabled'))
|
a.guide-btn.btn.btn-primary(href=("/teachers/course-solution/" + course.id + "/javascript") data-course-id=course.id data-course-name=course.get('name') data-event-action="Classes Guides Guide JavaScript" class=(me.isTeacher() || me.isAdmin() ? '': 'disabled'))
|
||||||
span(data-i18n="courses.view_guide_online")
|
span(data-i18n="courses.view_guide_online")
|
||||||
| — JavaScript
|
| — JavaScript
|
||||||
|
a.guide-btn.btn.btn-primary(href=("/teachers/course-solution/" + course.id + "/python") data-course-id=course.id data-course-name=course.get('name') data-event-action="Classes Guides Guide Python" class=(me.isTeacher() || me.isAdmin() ? '': 'disabled'))
|
||||||
|
span(data-i18n="courses.view_guide_online")
|
||||||
|
| — Python
|
||||||
|
|
|
@ -47,7 +47,7 @@ module.exports = class CourseDetailsView extends RootView
|
||||||
@supermodel.trackRequest(@classroom.fetch())
|
@supermodel.trackRequest(@classroom.fetch())
|
||||||
|
|
||||||
levelsLoaded = @supermodel.trackRequest(@levels.fetchForClassroomAndCourse(classroomID, @courseID, {
|
levelsLoaded = @supermodel.trackRequest(@levels.fetchForClassroomAndCourse(classroomID, @courseID, {
|
||||||
data: { project: 'concepts,practice,type,slug,name,original,description,shareable,i18n' }
|
data: { project: 'concepts,practice,primerLanguage,type,slug,name,original,description,shareable,i18n' }
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@supermodel.trackRequest($.when(levelsLoaded, sessionsLoaded).then(=>
|
@supermodel.trackRequest($.when(levelsLoaded, sessionsLoaded).then(=>
|
||||||
|
|
|
@ -120,7 +120,7 @@ module.exports = class TeacherClassView extends RootView
|
||||||
@supermodel.trackRequest @courseInstances.fetchForClassroom(classroomID)
|
@supermodel.trackRequest @courseInstances.fetchForClassroom(classroomID)
|
||||||
|
|
||||||
@levels = new Levels()
|
@levels = new Levels()
|
||||||
@supermodel.trackRequest @levels.fetchForClassroom(classroomID, {data: {project: 'original,concepts,practice,shareable,i18n'}})
|
@supermodel.trackRequest @levels.fetchForClassroom(classroomID, {data: {project: 'original,concepts,primerLanguage,practice,shareable,i18n'}})
|
||||||
|
|
||||||
@attachMediatorEvents()
|
@attachMediatorEvents()
|
||||||
window.tracker?.trackEvent 'Teachers Class Loaded', category: 'Teachers', classroomID: @classroom.id, ['Mixpanel']
|
window.tracker?.trackEvent 'Teachers Class Loaded', category: 'Teachers', classroomID: @classroom.id, ['Mixpanel']
|
||||||
|
@ -329,8 +329,10 @@ module.exports = class TeacherClassView extends RootView
|
||||||
courseOrder.push(course.id)
|
courseOrder.push(course.id)
|
||||||
csvContent = "data:text/csv;charset=utf-8,Username,Email,Total Playtime,#{courseLabels}Concepts\n"
|
csvContent = "data:text/csv;charset=utf-8,Username,Email,Total Playtime,#{courseLabels}Concepts\n"
|
||||||
levelCourseMap = {}
|
levelCourseMap = {}
|
||||||
|
language = @classroom.get('aceConfig')?.language
|
||||||
for trimCourse in @classroom.get('courses')
|
for trimCourse in @classroom.get('courses')
|
||||||
for trimLevel in trimCourse.levels
|
for trimLevel in trimCourse.levels
|
||||||
|
continue if language and trimLevel.primerLanguage is language
|
||||||
levelCourseMap[trimLevel.original] = @courses.get(trimCourse._id)
|
levelCourseMap[trimLevel.original] = @courses.get(trimCourse._id)
|
||||||
for student in @students.models
|
for student in @students.models
|
||||||
concepts = []
|
concepts = []
|
||||||
|
@ -448,9 +450,11 @@ module.exports = class TeacherClassView extends RootView
|
||||||
stats.totalPlaytime = if playtime then moment.duration(playtime, "seconds").humanize() else 0
|
stats.totalPlaytime = if playtime then moment.duration(playtime, "seconds").humanize() else 0
|
||||||
# TODO: Humanize differently ('1 hour' instead of 'an hour')
|
# TODO: Humanize differently ('1 hour' instead of 'an hour')
|
||||||
|
|
||||||
levelPracticeMap = {}
|
levelIncludeMap = {}
|
||||||
levelPracticeMap[level.id] = level.get('practice') ? false for level in @levels.models
|
language = @classroom.get('aceConfig')?.language
|
||||||
completeSessions = @classroom.sessions.filter (s) -> s.get('state')?.complete and not levelPracticeMap[s.get('levelID')]
|
for level in @levels.models
|
||||||
|
levelIncludeMap[level.get('original')] = not level.get('practice') and (not language? or level.get('primerLanguage') isnt language)
|
||||||
|
completeSessions = @classroom.sessions.filter (s) -> s.get('state')?.complete and levelIncludeMap[s.get('level')?.original]
|
||||||
stats.averageLevelsComplete = if @students.size() then (_.size(completeSessions) / @students.size()).toFixed(1) else 'N/A' # '
|
stats.averageLevelsComplete = if @students.size() then (_.size(completeSessions) / @students.size()).toFixed(1) else 'N/A' # '
|
||||||
stats.totalLevelsComplete = _.size(completeSessions)
|
stats.totalLevelsComplete = _.size(completeSessions)
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ module.exports = class TeacherCoursesView extends RootView
|
||||||
form = $(e.currentTarget).closest('.play-level-form')
|
form = $(e.currentTarget).closest('.play-level-form')
|
||||||
levelSlug = form.find('.level-select').val()
|
levelSlug = form.find('.level-select').val()
|
||||||
courseID = form.data('course-id')
|
courseID = form.data('course-id')
|
||||||
language = form.find('.language-select').val()
|
language = form.find('.language-select').val() or 'javascript'
|
||||||
window.tracker?.trackEvent 'Classes Guides Play Level', category: 'Teachers', courseID: courseID, language: language, levelSlug: levelSlug, ['Mixpanel']
|
window.tracker?.trackEvent 'Classes Guides Play Level', category: 'Teachers', courseID: courseID, language: language, levelSlug: levelSlug, ['Mixpanel']
|
||||||
url = "/play/level/#{levelSlug}?course=#{courseID}&codeLanguage=#{language}"
|
url = "/play/level/#{levelSlug}?course=#{courseID}&codeLanguage=#{language}"
|
||||||
firstLevelSlug = @campaigns.get(@courses.at(0).get('campaignID')).getLevels().at(0).get('slug')
|
firstLevelSlug = @campaigns.get(@courses.at(0).get('campaignID')).getLevels().at(0).get('slug')
|
||||||
|
|
|
@ -31,7 +31,7 @@ module.exports = class TeacherCourseSolutionView extends RootView
|
||||||
|
|
||||||
onLoaded: ->
|
onLoaded: ->
|
||||||
for level in @levels?.models
|
for level in @levels?.models
|
||||||
articles = level.get('documentation').specificArticles
|
articles = level.get('documentation')?.specificArticles
|
||||||
if articles
|
if articles
|
||||||
guide = articles.filter((x) => x.name == "Overview").pop()
|
guide = articles.filter((x) => x.name == "Overview").pop()
|
||||||
level.set 'guide', marked(@hideWrongLanguage(guide.body)) if guide
|
level.set 'guide', marked(@hideWrongLanguage(guide.body)) if guide
|
||||||
|
|
|
@ -61,7 +61,12 @@ module.exports =
|
||||||
for level in course.levels
|
for level in course.levels
|
||||||
levelOriginals.push(level.original)
|
levelOriginals.push(level.original)
|
||||||
|
|
||||||
levels = yield Level.find({ original: { $in: levelOriginals }, slug: { $exists: true }}).select(parse.getProjectFromReq(req))
|
query = {$and: [
|
||||||
|
{original: { $in: levelOriginals }}
|
||||||
|
{$or: [{primerLanguage: {$exists: false}}, {primerLanguage: { $ne: classroom.get('aceConfig')?.language }}]}
|
||||||
|
{slug: { $exists: true }}
|
||||||
|
]}
|
||||||
|
levels = yield Level.find(query).select(parse.getProjectFromReq(req))
|
||||||
levels = (level.toObject({ req: req }) for level in levels)
|
levels = (level.toObject({ req: req }) for level in levels)
|
||||||
|
|
||||||
# maintain course order
|
# maintain course order
|
||||||
|
@ -84,14 +89,19 @@ module.exports =
|
||||||
for level in course.levels
|
for level in course.levels
|
||||||
levelOriginals.push(level.original)
|
levelOriginals.push(level.original)
|
||||||
|
|
||||||
levels = yield Level.find({ original: { $in: levelOriginals }, slug: { $exists: true }}).select(parse.getProjectFromReq(req))
|
query = {$and: [
|
||||||
|
{original: { $in: levelOriginals }}
|
||||||
|
{$or: [{primerLanguage: {$exists: false}}, {primerLanguage: { $ne: classroom.get('aceConfig')?.language }}]}
|
||||||
|
{slug: { $exists: true }}
|
||||||
|
]}
|
||||||
|
levels = yield Level.find(query).select(parse.getProjectFromReq(req))
|
||||||
levels = (level.toObject({ req: req }) for level in levels)
|
levels = (level.toObject({ req: req }) for level in levels)
|
||||||
|
|
||||||
# maintain course order
|
# maintain course order
|
||||||
levelMap = {}
|
levelMap = {}
|
||||||
for level in levels
|
for level in levels
|
||||||
levelMap[level.original] = level
|
levelMap[level.original] = level
|
||||||
levels = (levelMap[levelOriginal.toString()] for levelOriginal in levelOriginals)
|
levels = (levelMap[levelOriginal.toString()] for levelOriginal in levelOriginals when levelMap[levelOriginal.toString()])
|
||||||
|
|
||||||
res.status(200).send(levels)
|
res.status(200).send(levels)
|
||||||
|
|
||||||
|
@ -143,7 +153,7 @@ module.exports =
|
||||||
database.assignBody(req, classroom)
|
database.assignBody(req, classroom)
|
||||||
|
|
||||||
# Copy over data from how courses are right now
|
# Copy over data from how courses are right now
|
||||||
coursesData = yield module.exports.generateCoursesData(req)
|
coursesData = yield module.exports.generateCoursesData(classroom.get('aceConfig')?.language, req.user?.isAdmin())
|
||||||
classroom.set('courses', coursesData)
|
classroom.set('courses', coursesData)
|
||||||
|
|
||||||
# finish
|
# finish
|
||||||
|
@ -159,15 +169,15 @@ module.exports =
|
||||||
unless req.user._id.equals(classroom.get('ownerID'))
|
unless req.user._id.equals(classroom.get('ownerID'))
|
||||||
throw new errors.Forbidden('Only the owner may update their classroom content')
|
throw new errors.Forbidden('Only the owner may update their classroom content')
|
||||||
|
|
||||||
coursesData = yield module.exports.generateCoursesData(req)
|
coursesData = yield module.exports.generateCoursesData(classroom.get('aceConfig')?.language, req.user?.isAdmin())
|
||||||
classroom.set('courses', coursesData)
|
classroom.set('courses', coursesData)
|
||||||
classroom = yield classroom.save()
|
classroom = yield classroom.save()
|
||||||
res.status(200).send(classroom.toObject({req: req}))
|
res.status(200).send(classroom.toObject({req: req}))
|
||||||
|
|
||||||
generateCoursesData: co.wrap (req) ->
|
generateCoursesData: co.wrap (classLanguage, isAdmin) ->
|
||||||
# helper function for generating the latest version of courses
|
# helper function for generating the latest version of courses
|
||||||
query = {}
|
query = {}
|
||||||
query = {releasePhase: 'released'} unless req.user?.isAdmin()
|
query = {releasePhase: 'released'} unless isAdmin
|
||||||
courses = yield Course.find(query)
|
courses = yield Course.find(query)
|
||||||
courses = Course.sortCourses courses
|
courses = Course.sortCourses courses
|
||||||
campaigns = yield Campaign.find({_id: {$in: (course.get('campaignID') for course in courses)}})
|
campaigns = yield Campaign.find({_id: {$in: (course.get('campaignID') for course in courses)}})
|
||||||
|
@ -180,8 +190,9 @@ module.exports =
|
||||||
levels = _.values(campaign.get('levels'))
|
levels = _.values(campaign.get('levels'))
|
||||||
levels = _.sortBy(levels, 'campaignIndex')
|
levels = _.sortBy(levels, 'campaignIndex')
|
||||||
for level in levels
|
for level in levels
|
||||||
|
continue if classLanguage and level.primerLanguage is classLanguage
|
||||||
levelData = { original: mongoose.Types.ObjectId(level.original) }
|
levelData = { original: mongoose.Types.ObjectId(level.original) }
|
||||||
_.extend(levelData, _.pick(level, 'type', 'slug', 'name', 'practice', 'practiceThresholdMinutes', 'shareable'))
|
_.extend(levelData, _.pick(level, 'type', 'slug', 'name', 'practice', 'practiceThresholdMinutes', 'primerLanguage', 'shareable'))
|
||||||
courseData.levels.push(levelData)
|
courseData.levels.push(levelData)
|
||||||
coursesData.push(courseData)
|
coursesData.push(courseData)
|
||||||
return coursesData
|
return coursesData
|
||||||
|
|
|
@ -87,6 +87,8 @@ module.exports =
|
||||||
courseID = courseInstance.get('courseID')
|
courseID = courseInstance.get('courseID')
|
||||||
courseLevels = []
|
courseLevels = []
|
||||||
courseLevels = course.levels for course in classroom.get('courses') or [] when courseID.equals(course._id)
|
courseLevels = course.levels for course in classroom.get('courses') or [] when courseID.equals(course._id)
|
||||||
|
classLanguage = classroom.get('aceConfig')?.language
|
||||||
|
_.remove(courseLevels, (level) -> level.primerLanguage is classLanguage) if classLanguage
|
||||||
|
|
||||||
# Get level completions and playtime
|
# Get level completions and playtime
|
||||||
currentLevelSession = null
|
currentLevelSession = null
|
||||||
|
|
|
@ -90,8 +90,15 @@ describe 'POST /db/classroom', ->
|
||||||
[res, body] = yield request.postAsync({uri: getURL('/db/level'), json: levelJSONC})
|
[res, body] = yield request.postAsync({uri: getURL('/db/level'), json: levelJSONC})
|
||||||
expect(res.statusCode).toBe(200)
|
expect(res.statusCode).toBe(200)
|
||||||
@levelC = yield Level.findById(res.body._id)
|
@levelC = yield Level.findById(res.body._id)
|
||||||
|
levelJSONJSPrimer1 = { name: 'JS Primer 1', permissions: [{access: 'owner', target: admin.id}], type: 'hero', primerLanguage: 'javascript' }
|
||||||
|
[res, body] = yield request.postAsync({uri: getURL('/db/level'), json: levelJSONJSPrimer1})
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
@levelJSPrimer1 = yield Level.findById(res.body._id)
|
||||||
|
|
||||||
campaignJSON = { name: 'Campaign', levels: {} }
|
campaignJSON = { name: 'Campaign', levels: {} }
|
||||||
|
paredLevelJSPrimer1 = _.pick(@levelJSPrimer1.toObject(), 'name', 'original', 'type', 'slug', 'primerLanguage')
|
||||||
|
paredLevelJSPrimer1.campaignIndex = 3
|
||||||
|
campaignJSON.levels[@levelJSPrimer1.get('original').toString()] = paredLevelJSPrimer1
|
||||||
paredLevelC = _.pick(@levelC.toObject(), 'name', 'original', 'type', 'slug', 'practice')
|
paredLevelC = _.pick(@levelC.toObject(), 'name', 'original', 'type', 'slug', 'practice')
|
||||||
paredLevelC.campaignIndex = 2
|
paredLevelC.campaignIndex = 2
|
||||||
campaignJSON.levels[@levelC.get('original').toString()] = paredLevelC
|
campaignJSON.levels[@levelC.get('original').toString()] = paredLevelC
|
||||||
|
@ -134,18 +141,43 @@ describe 'POST /db/classroom', ->
|
||||||
expect(res.statusCode).toBe(403)
|
expect(res.statusCode).toBe(403)
|
||||||
done()
|
done()
|
||||||
|
|
||||||
it 'makes a copy of the list of all levels in all courses', utils.wrap (done) ->
|
describe 'when javascript classroom', ->
|
||||||
|
|
||||||
|
beforeEach utils.wrap (done) ->
|
||||||
teacher = yield utils.initUser({role: 'teacher'})
|
teacher = yield utils.initUser({role: 'teacher'})
|
||||||
yield utils.loginUser(teacher)
|
yield utils.loginUser(teacher)
|
||||||
data = { name: 'Classroom 2' }
|
data = { name: 'Classroom 2', aceConfig: { language: 'javascript' } }
|
||||||
[res, body] = yield request.postAsync {uri: classroomsURL, json: data }
|
[res, body] = yield request.postAsync {uri: classroomsURL, json: data }
|
||||||
classroom = yield Classroom.findById(res.body._id)
|
@classroom = yield Classroom.findById(res.body._id)
|
||||||
expect(classroom.get('courses')[0].levels[0].original.toString()).toBe(@levelA.get('original').toString())
|
|
||||||
expect(classroom.get('courses')[0].levels[0].type).toBe('course')
|
|
||||||
expect(classroom.get('courses')[0].levels[0].slug).toBe('level-a')
|
|
||||||
expect(classroom.get('courses')[0].levels[0].name).toBe('Level A')
|
|
||||||
done()
|
done()
|
||||||
|
|
||||||
|
it 'makes a copy of the list of all levels in all courses', utils.wrap (done) ->
|
||||||
|
expect(@classroom.get('courses')[0].levels.length).toEqual(3)
|
||||||
|
expect(@classroom.get('courses')[0].levels[0].original.toString()).toBe(@levelA.get('original').toString())
|
||||||
|
expect(@classroom.get('courses')[0].levels[0].type).toBe('course')
|
||||||
|
expect(@classroom.get('courses')[0].levels[0].slug).toBe('level-a')
|
||||||
|
expect(@classroom.get('courses')[0].levels[0].name).toBe('Level A')
|
||||||
|
done()
|
||||||
|
|
||||||
|
describe 'when python classroom', ->
|
||||||
|
|
||||||
|
beforeEach utils.wrap (done) ->
|
||||||
|
teacher = yield utils.initUser({role: 'teacher'})
|
||||||
|
yield utils.loginUser(teacher)
|
||||||
|
data = { name: 'Classroom 2', aceConfig: { language: 'python' } }
|
||||||
|
[res, body] = yield request.postAsync {uri: classroomsURL, json: data }
|
||||||
|
@classroom = yield Classroom.findById(res.body._id)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'makes a copy all levels in all courses', utils.wrap (done) ->
|
||||||
|
expect(@classroom.get('courses')[0].levels.length).toEqual(4)
|
||||||
|
expect(@classroom.get('courses')[0].levels[0].original.toString()).toBe(@levelA.get('original').toString())
|
||||||
|
expect(@classroom.get('courses')[0].levels[0].type).toBe('course')
|
||||||
|
expect(@classroom.get('courses')[0].levels[0].slug).toBe('level-a')
|
||||||
|
expect(@classroom.get('courses')[0].levels[0].name).toBe('Level A')
|
||||||
|
done()
|
||||||
|
|
||||||
|
|
||||||
describe 'when there are unreleased courses', ->
|
describe 'when there are unreleased courses', ->
|
||||||
beforeEach utils.wrap (done) ->
|
beforeEach utils.wrap (done) ->
|
||||||
admin = yield utils.initAdmin()
|
admin = yield utils.initAdmin()
|
||||||
|
@ -197,39 +229,6 @@ describe 'POST /db/classroom', ->
|
||||||
expect(classroom.get('courses')[0].levels[0].name).toBe('Level A')
|
expect(classroom.get('courses')[0].levels[0].name).toBe('Level A')
|
||||||
done()
|
done()
|
||||||
|
|
||||||
describe 'GET /db/classroom/:handle/levels', ->
|
|
||||||
|
|
||||||
beforeEach utils.wrap (done) ->
|
|
||||||
yield utils.clearModels [User, Classroom, Course, Level, Campaign]
|
|
||||||
admin = yield utils.initAdmin()
|
|
||||||
yield utils.loginUser(admin)
|
|
||||||
levelJSON = { name: 'King\'s Peak 3', permissions: [{access: 'owner', target: admin.id}], type: 'course' }
|
|
||||||
[res, body] = yield request.postAsync({uri: getURL('/db/level'), json: levelJSON})
|
|
||||||
expect(res.statusCode).toBe(200)
|
|
||||||
@level = yield Level.findById(res.body._id)
|
|
||||||
campaignJSON = { name: 'Campaign', levels: {} }
|
|
||||||
paredLevel = _.pick(res.body, 'name', 'original', 'type')
|
|
||||||
campaignJSON.levels[res.body.original] = paredLevel
|
|
||||||
[res, body] = yield request.postAsync({uri: getURL('/db/campaign'), json: campaignJSON})
|
|
||||||
@campaign = yield Campaign.findById(res.body._id)
|
|
||||||
@course = Course({name: 'Course', campaignID: @campaign._id, releasePhase: 'released'})
|
|
||||||
yield @course.save()
|
|
||||||
teacher = yield utils.initUser({role: 'teacher'})
|
|
||||||
yield utils.loginUser(teacher)
|
|
||||||
data = { name: 'Classroom 1' }
|
|
||||||
[res, body] = yield request.postAsync {uri: classroomsURL, json: data }
|
|
||||||
expect(res.statusCode).toBe(201)
|
|
||||||
@classroom = yield Classroom.findById(res.body._id)
|
|
||||||
done()
|
|
||||||
|
|
||||||
it 'returns all levels referenced in in the classroom\'s copy of course levels', utils.wrap (done) ->
|
|
||||||
[res, body] = yield request.getAsync { uri: getURL("/db/classroom/#{@classroom.id}/levels"), json: true }
|
|
||||||
expect(res.statusCode).toBe(200)
|
|
||||||
levels = res.body
|
|
||||||
expect(levels.length).toBe(1)
|
|
||||||
expect(levels[0].name).toBe("King's Peak 3")
|
|
||||||
done()
|
|
||||||
|
|
||||||
describe 'GET /db/classroom/:handle/levels', ->
|
describe 'GET /db/classroom/:handle/levels', ->
|
||||||
|
|
||||||
beforeEach utils.wrap (done) ->
|
beforeEach utils.wrap (done) ->
|
||||||
|
@ -249,6 +248,12 @@ describe 'GET /db/classroom/:handle/levels', ->
|
||||||
@levelB = yield Level.findById(res.body._id)
|
@levelB = yield Level.findById(res.body._id)
|
||||||
paredLevelB = _.pick(res.body, 'name', 'original', 'type')
|
paredLevelB = _.pick(res.body, 'name', 'original', 'type')
|
||||||
|
|
||||||
|
levelJSON = { name: 'JS Primer 1', permissions: [{access: 'owner', target: admin.id}], type: 'course', primerLanguage: 'javascript' }
|
||||||
|
[res, body] = yield request.postAsync({uri: getURL('/db/level'), json: levelJSON})
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
@levelJSPrimer1 = yield Level.findById(res.body._id)
|
||||||
|
paredLevelJSPrimer1 = _.pick(res.body, 'name', 'original', 'type')
|
||||||
|
|
||||||
campaignJSONA = { name: 'Campaign A', levels: {} }
|
campaignJSONA = { name: 'Campaign A', levels: {} }
|
||||||
campaignJSONA.levels[paredLevelA.original] = paredLevelA
|
campaignJSONA.levels[paredLevelA.original] = paredLevelA
|
||||||
[res, body] = yield request.postAsync({uri: getURL('/db/campaign'), json: campaignJSONA})
|
[res, body] = yield request.postAsync({uri: getURL('/db/campaign'), json: campaignJSONA})
|
||||||
|
@ -256,6 +261,7 @@ describe 'GET /db/classroom/:handle/levels', ->
|
||||||
|
|
||||||
campaignJSONB = { name: 'Campaign B', levels: {} }
|
campaignJSONB = { name: 'Campaign B', levels: {} }
|
||||||
campaignJSONB.levels[paredLevelB.original] = paredLevelB
|
campaignJSONB.levels[paredLevelB.original] = paredLevelB
|
||||||
|
campaignJSONB.levels[paredLevelJSPrimer1.original] = paredLevelJSPrimer1
|
||||||
[res, body] = yield request.postAsync({uri: getURL('/db/campaign'), json: campaignJSONB})
|
[res, body] = yield request.postAsync({uri: getURL('/db/campaign'), json: campaignJSONB})
|
||||||
@campaignB = yield Campaign.findById(res.body._id)
|
@campaignB = yield Campaign.findById(res.body._id)
|
||||||
|
|
||||||
|
@ -265,9 +271,14 @@ describe 'GET /db/classroom/:handle/levels', ->
|
||||||
@courseB = Course({name: 'Course B', campaignID: @campaignB._id, releasePhase: 'released'})
|
@courseB = Course({name: 'Course B', campaignID: @campaignB._id, releasePhase: 'released'})
|
||||||
yield @courseB.save()
|
yield @courseB.save()
|
||||||
|
|
||||||
|
done()
|
||||||
|
|
||||||
|
describe 'when javascript classroom', ->
|
||||||
|
|
||||||
|
beforeEach utils.wrap (done) ->
|
||||||
teacher = yield utils.initUser({role: 'teacher'})
|
teacher = yield utils.initUser({role: 'teacher'})
|
||||||
yield utils.loginUser(teacher)
|
yield utils.loginUser(teacher)
|
||||||
data = { name: 'Classroom 1' }
|
data = { name: 'Classroom 1', aceConfig: { language: 'javascript' } }
|
||||||
[res, body] = yield request.postAsync {uri: classroomsURL, json: data }
|
[res, body] = yield request.postAsync {uri: classroomsURL, json: data }
|
||||||
expect(res.statusCode).toBe(201)
|
expect(res.statusCode).toBe(201)
|
||||||
@classroom = yield Classroom.findById(res.body._id)
|
@classroom = yield Classroom.findById(res.body._id)
|
||||||
|
@ -293,6 +304,37 @@ describe 'GET /db/classroom/:handle/levels', ->
|
||||||
|
|
||||||
done()
|
done()
|
||||||
|
|
||||||
|
describe 'when python classroom', ->
|
||||||
|
|
||||||
|
beforeEach utils.wrap (done) ->
|
||||||
|
teacher = yield utils.initUser({role: 'teacher'})
|
||||||
|
yield utils.loginUser(teacher)
|
||||||
|
data = { name: 'Classroom 1', aceConfig: { language: 'python' } }
|
||||||
|
[res, body] = yield request.postAsync {uri: classroomsURL, json: data }
|
||||||
|
expect(res.statusCode).toBe(201)
|
||||||
|
@classroom = yield Classroom.findById(res.body._id)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'returns all levels referenced in in the classroom\'s copy of course levels', utils.wrap (done) ->
|
||||||
|
[res, body] = yield request.getAsync { uri: getURL("/db/classroom/#{@classroom.id}/levels"), json: true }
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
levels = res.body
|
||||||
|
expect(levels.length).toBe(3)
|
||||||
|
|
||||||
|
[res, body] = yield request.getAsync { uri: getURL("/db/classroom/#{@classroom.id}/courses/#{@courseA.id}/levels"), json: true }
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
levels = res.body
|
||||||
|
expect(levels.length).toBe(1)
|
||||||
|
expect(levels[0].original).toBe(@levelA.get('original').toString())
|
||||||
|
|
||||||
|
[res, body] = yield request.getAsync { uri: getURL("/db/classroom/#{@classroom.id}/courses/#{@courseB.id}/levels"), json: true }
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
levels = res.body
|
||||||
|
expect(levels.length).toBe(2)
|
||||||
|
expect(levels[0].original).toBe(@levelB.get('original').toString())
|
||||||
|
expect(levels[1].original).toBe(@levelJSPrimer1.get('original').toString())
|
||||||
|
|
||||||
|
done()
|
||||||
|
|
||||||
describe 'PUT /db/classroom', ->
|
describe 'PUT /db/classroom', ->
|
||||||
|
|
||||||
|
|
|
@ -246,7 +246,7 @@ describe 'GET /db/course_instance/:handle/levels/:levelOriginal/next', ->
|
||||||
yield utils.clearModels [User, Classroom, Course, Level, Campaign]
|
yield utils.clearModels [User, Classroom, Course, Level, Campaign]
|
||||||
admin = yield utils.initAdmin()
|
admin = yield utils.initAdmin()
|
||||||
yield utils.loginUser(admin)
|
yield utils.loginUser(admin)
|
||||||
teacher = yield utils.initUser({role: 'teacher'})
|
@teacher = yield utils.initUser({role: 'teacher'})
|
||||||
|
|
||||||
levelJSON = { name: 'A', permissions: [{access: 'owner', target: admin.id}], type: 'course' }
|
levelJSON = { name: 'A', permissions: [{access: 'owner', target: admin.id}], type: 'course' }
|
||||||
[res, body] = yield request.postAsync({uri: getURL('/db/level'), json: levelJSON})
|
[res, body] = yield request.postAsync({uri: getURL('/db/level'), json: levelJSON})
|
||||||
|
@ -255,7 +255,7 @@ describe 'GET /db/course_instance/:handle/levels/:levelOriginal/next', ->
|
||||||
paredLevelA = _.pick(res.body, 'name', 'original', 'type')
|
paredLevelA = _.pick(res.body, 'name', 'original', 'type')
|
||||||
|
|
||||||
@sessionA = new LevelSession
|
@sessionA = new LevelSession
|
||||||
creator: teacher.id
|
creator: @teacher.id
|
||||||
level: original: @levelA.get('original').toString()
|
level: original: @levelA.get('original').toString()
|
||||||
permissions: simplePermissions
|
permissions: simplePermissions
|
||||||
state: complete: true
|
state: complete: true
|
||||||
|
@ -268,7 +268,7 @@ describe 'GET /db/course_instance/:handle/levels/:levelOriginal/next', ->
|
||||||
paredLevelB = _.pick(res.body, 'name', 'original', 'type')
|
paredLevelB = _.pick(res.body, 'name', 'original', 'type')
|
||||||
|
|
||||||
@sessionB = new LevelSession
|
@sessionB = new LevelSession
|
||||||
creator: teacher.id
|
creator: @teacher.id
|
||||||
level: original: @levelB.get('original').toString()
|
level: original: @levelB.get('original').toString()
|
||||||
permissions: simplePermissions
|
permissions: simplePermissions
|
||||||
yield @sessionB.save()
|
yield @sessionB.save()
|
||||||
|
@ -279,9 +279,22 @@ describe 'GET /db/course_instance/:handle/levels/:levelOriginal/next', ->
|
||||||
@levelC = yield Level.findById(res.body._id)
|
@levelC = yield Level.findById(res.body._id)
|
||||||
paredLevelC = _.pick(res.body, 'name', 'original', 'type')
|
paredLevelC = _.pick(res.body, 'name', 'original', 'type')
|
||||||
|
|
||||||
|
levelJSON = { name: 'JS Primer 1', permissions: [{access: 'owner', target: admin.id}], type: 'course', primerLanguage: 'javascript' }
|
||||||
|
[res, body] = yield request.postAsync({uri: getURL('/db/level'), json: levelJSON})
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
@levelJSPrimer1 = yield Level.findById(res.body._id)
|
||||||
|
paredLevelJSPrimer1 = _.pick(res.body, 'name', 'original', 'primerLanguage', 'type')
|
||||||
|
|
||||||
|
@sessionJSPrimer1 = new LevelSession
|
||||||
|
creator: @teacher.id
|
||||||
|
level: original: @levelJSPrimer1.get('original').toString()
|
||||||
|
permissions: simplePermissions
|
||||||
|
yield @sessionJSPrimer1.save()
|
||||||
|
|
||||||
campaignJSONA = { name: 'Campaign A', levels: {} }
|
campaignJSONA = { name: 'Campaign A', levels: {} }
|
||||||
campaignJSONA.levels[paredLevelA.original] = paredLevelA
|
campaignJSONA.levels[paredLevelA.original] = paredLevelA
|
||||||
campaignJSONA.levels[paredLevelB.original] = paredLevelB
|
campaignJSONA.levels[paredLevelB.original] = paredLevelB
|
||||||
|
campaignJSONA.levels[paredLevelJSPrimer1.original] = paredLevelJSPrimer1
|
||||||
[res, body] = yield request.postAsync({uri: getURL('/db/campaign'), json: campaignJSONA})
|
[res, body] = yield request.postAsync({uri: getURL('/db/campaign'), json: campaignJSONA})
|
||||||
@campaignA = yield Campaign.findById(res.body._id)
|
@campaignA = yield Campaign.findById(res.body._id)
|
||||||
|
|
||||||
|
@ -296,8 +309,12 @@ describe 'GET /db/course_instance/:handle/levels/:levelOriginal/next', ->
|
||||||
@courseB = Course({name: 'Course B', campaignID: @campaignB._id, releasePhase: 'released'})
|
@courseB = Course({name: 'Course B', campaignID: @campaignB._id, releasePhase: 'released'})
|
||||||
yield @courseB.save()
|
yield @courseB.save()
|
||||||
|
|
||||||
yield utils.loginUser(teacher)
|
done()
|
||||||
data = { name: 'Classroom 1' }
|
|
||||||
|
describe 'when javascript classroom', ->
|
||||||
|
beforeEach utils.wrap (done) ->
|
||||||
|
yield utils.loginUser(@teacher)
|
||||||
|
data = { name: 'Classroom 1', aceConfig: { language: 'javascript' } }
|
||||||
classroomsURL = getURL('/db/classroom')
|
classroomsURL = getURL('/db/classroom')
|
||||||
[res, body] = yield request.postAsync {uri: classroomsURL, json: data }
|
[res, body] = yield request.postAsync {uri: classroomsURL, json: data }
|
||||||
expect(res.statusCode).toBe(201)
|
expect(res.statusCode).toBe(201)
|
||||||
|
@ -334,6 +351,45 @@ describe 'GET /db/course_instance/:handle/levels/:levelOriginal/next', ->
|
||||||
expect(res.statusCode).toBe(404)
|
expect(res.statusCode).toBe(404)
|
||||||
done()
|
done()
|
||||||
|
|
||||||
|
it 'returns 404 if the given level is no applicable primer level', utils.wrap (done) ->
|
||||||
|
[res, body] = yield request.getAsync { uri: utils.getURL("/db/course_instance/#{@courseInstanceA.id}/levels/#{@levelJSPrimer1.id}/sessions/#{@sessionJSPrimer1.id}/next"), json: true }
|
||||||
|
expect(res.statusCode).toBe(404)
|
||||||
|
done()
|
||||||
|
|
||||||
|
describe 'when python classroom', ->
|
||||||
|
beforeEach utils.wrap (done) ->
|
||||||
|
yield utils.loginUser(@teacher)
|
||||||
|
data = { name: 'Classroom 1', aceConfig: { language: 'python' } }
|
||||||
|
classroomsURL = getURL('/db/classroom')
|
||||||
|
[res, body] = yield request.postAsync {uri: classroomsURL, json: data }
|
||||||
|
expect(res.statusCode).toBe(201)
|
||||||
|
@classroom = yield Classroom.findById(res.body._id)
|
||||||
|
|
||||||
|
url = getURL('/db/course_instance')
|
||||||
|
|
||||||
|
dataA = { name: 'Some Name', courseID: @courseA.id, classroomID: @classroom.id }
|
||||||
|
[res, body] = yield request.postAsync {uri: url, json: dataA}
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
@courseInstanceA = yield CourseInstance.findById(res.body._id)
|
||||||
|
|
||||||
|
dataB = { name: 'Some Other Name', courseID: @courseB.id, classroomID: @classroom.id }
|
||||||
|
[res, body] = yield request.postAsync {uri: url, json: dataB}
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
@courseInstanceB = yield CourseInstance.findById(res.body._id)
|
||||||
|
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'returns the next level for the course in the linked classroom', utils.wrap (done) ->
|
||||||
|
[res, body] = yield request.getAsync { uri: utils.getURL("/db/course_instance/#{@courseInstanceA.id}/levels/#{@levelB.id}/sessions/#{@sessionB.id}/next"), json: true }
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
expect(res.body.original).toBe(@levelJSPrimer1.original.toString())
|
||||||
|
done()
|
||||||
|
|
||||||
|
it 'returns empty object if the given level is the last level in its course', utils.wrap (done) ->
|
||||||
|
[res, body] = yield request.getAsync { uri: utils.getURL("/db/course_instance/#{@courseInstanceA.id}/levels/#{@levelJSPrimer1.id}/sessions/#{@sessionJSPrimer1.id}/next"), json: true }
|
||||||
|
expect(res.statusCode).toBe(200)
|
||||||
|
expect(res.body).toEqual({})
|
||||||
|
done()
|
||||||
|
|
||||||
describe 'GET /db/course_instance/:handle/classroom', ->
|
describe 'GET /db/course_instance/:handle/classroom', ->
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,7 @@ module.exports = {
|
||||||
break if not courseAttrs
|
break if not courseAttrs
|
||||||
course ?= @makeCourse()
|
course ?= @makeCourse()
|
||||||
levels ?= new Levels()
|
levels ?= new Levels()
|
||||||
courseAttrs.levels = (level.pick('_id', 'slug', 'name', 'original', 'type') for level in levels.models)
|
courseAttrs.levels = (level.pick('_id', 'slug', 'name', 'original', 'primerLanguage', 'type') for level in levels.models)
|
||||||
|
|
||||||
# populate members
|
# populate members
|
||||||
if not attrs.members
|
if not attrs.members
|
||||||
|
@ -111,6 +111,7 @@ module.exports = {
|
||||||
original: level.get('original'),
|
original: level.get('original'),
|
||||||
creator: creator.id,
|
creator: creator.id,
|
||||||
}, attrs)
|
}, attrs)
|
||||||
|
attrs.level.primerLanguage = level.get('primerLanguage') if level.get('primerLanguage')
|
||||||
return new LevelSession(attrs)
|
return new LevelSession(attrs)
|
||||||
|
|
||||||
makeCourseInstance: (attrs, sources={}) ->
|
makeCourseInstance: (attrs, sources={}) ->
|
||||||
|
|
|
@ -41,7 +41,13 @@ describe 'TeacherClassView', ->
|
||||||
factories.makeUser({name: 'Ebner'}, {prepaid: expired})
|
factories.makeUser({name: 'Ebner'}, {prepaid: expired})
|
||||||
])
|
])
|
||||||
@levels = new Levels(_.times(2, -> factories.makeLevel({ concepts: ['basic_syntax', 'arguments', 'functions'] })))
|
@levels = new Levels(_.times(2, -> factories.makeLevel({ concepts: ['basic_syntax', 'arguments', 'functions'] })))
|
||||||
@classroom = factories.makeClassroom({}, { courses: @releasedCourses, members: @students, levels: [@levels, new Levels()] })
|
@levels.push(factories.makeLevel({ concepts: ['basic_syntax', 'arguments', 'functions'], primerLanguage: 'javascript' }))
|
||||||
|
|
||||||
|
_.defer done
|
||||||
|
|
||||||
|
describe 'when python classroom', ->
|
||||||
|
beforeEach (done) ->
|
||||||
|
@classroom = factories.makeClassroom({ aceConfig: { language: 'python' }}, { courses: @releasedCourses, members: @students, levels: [@levels, new Levels()] })
|
||||||
@courseInstances = new CourseInstances([
|
@courseInstances = new CourseInstances([
|
||||||
factories.makeCourseInstance({}, { course: @releasedCourses.first(), @classroom, members: @students })
|
factories.makeCourseInstance({}, { course: @releasedCourses.first(), @classroom, members: @students })
|
||||||
factories.makeCourseInstance({}, { course: @releasedCourses.last(), @classroom, members: @students })
|
factories.makeCourseInstance({}, { course: @releasedCourses.last(), @classroom, members: @students })
|
||||||
|
@ -75,7 +81,6 @@ describe 'TeacherClassView', ->
|
||||||
it 'has contents', ->
|
it 'has contents', ->
|
||||||
expect(@view.$el.children().length).toBeGreaterThan(0)
|
expect(@view.$el.children().length).toBeGreaterThan(0)
|
||||||
|
|
||||||
|
|
||||||
# it "shows the classroom's name and description"
|
# it "shows the classroom's name and description"
|
||||||
# it "shows the classroom's join code"
|
# it "shows the classroom's join code"
|
||||||
|
|
||||||
|
@ -124,6 +129,63 @@ describe 'TeacherClassView', ->
|
||||||
expect(users.size()).toBe(1)
|
expect(users.size()).toBe(1)
|
||||||
expect(users.first().id).toBe(@view.students.first().id)
|
expect(users.first().id).toBe(@view.students.first().id)
|
||||||
|
|
||||||
|
describe 'Export Student Progress (CSV) button', ->
|
||||||
|
it 'downloads a CSV file', ->
|
||||||
|
spyOn(window, 'open').and.callFake (encodedCSV) =>
|
||||||
|
progressData = decodeURI(encodedCSV)
|
||||||
|
CSVHeader = 'data:text\/csv;charset=utf-8,'
|
||||||
|
expect(progressData).toMatch new RegExp('^' + CSVHeader)
|
||||||
|
lines = progressData.slice(CSVHeader.length).split('\n')
|
||||||
|
expect(lines.length).toBe(@students.length + 1)
|
||||||
|
for line in lines
|
||||||
|
simplerLine = line.replace(/"[^"]+"/g, '""')
|
||||||
|
# Username,Email,Total Playtime, [CS1-? Playtime], Concepts
|
||||||
|
expect(simplerLine.match(/[^,]+/g).length).toBe(3 + @releasedCourses.length + 1)
|
||||||
|
if simplerLine.match new RegExp(@finishedStudent.get('email'))
|
||||||
|
expect(simplerLine).toMatch /3 minutes,3 minutes,0/
|
||||||
|
else if simplerLine.match new RegExp(@unfinishedStudent.get('email'))
|
||||||
|
expect(simplerLine).toMatch /a minute,a minute,0/
|
||||||
|
else if simplerLine.match /@/
|
||||||
|
expect(simplerLine).toMatch /0,0,0/
|
||||||
|
return true
|
||||||
|
@view.$('.export-student-progress-btn').click()
|
||||||
|
expect(window.open).toHaveBeenCalled()
|
||||||
|
|
||||||
|
describe 'when javascript classroom', ->
|
||||||
|
beforeEach (done) ->
|
||||||
|
@classroom = factories.makeClassroom({ aceConfig: { language: 'javascript' }}, { courses: @releasedCourses, members: @students, levels: [@levels, new Levels()]})
|
||||||
|
@courseInstances = new CourseInstances([
|
||||||
|
factories.makeCourseInstance({}, { course: @releasedCourses.first(), @classroom, members: @students })
|
||||||
|
factories.makeCourseInstance({}, { course: @releasedCourses.last(), @classroom, members: @students })
|
||||||
|
])
|
||||||
|
|
||||||
|
sessions = []
|
||||||
|
@finishedStudent = @students.first()
|
||||||
|
@unfinishedStudent = @students.last()
|
||||||
|
classLanguage = @classroom.get('aceConfig')?.language
|
||||||
|
for level in @levels.models
|
||||||
|
continue if classLanguage and classLanguage is level.get('primerLanguage')
|
||||||
|
sessions.push(factories.makeLevelSession(
|
||||||
|
{state: {complete: true}, playtime: 60},
|
||||||
|
{level, creator: @finishedStudent})
|
||||||
|
)
|
||||||
|
sessions.push(factories.makeLevelSession(
|
||||||
|
{state: {complete: true}, playtime: 60},
|
||||||
|
{level: @levels.first(), creator: @unfinishedStudent})
|
||||||
|
)
|
||||||
|
@levelSessions = new LevelSessions(sessions)
|
||||||
|
|
||||||
|
@view = new TeacherClassView({}, @courseInstances.first().id)
|
||||||
|
@view.classroom.fakeRequests[0].respondWith({ status: 200, responseText: @classroom.stringify() })
|
||||||
|
@view.courses.fakeRequests[0].respondWith({ status: 200, responseText: @courses.stringify() })
|
||||||
|
@view.courseInstances.fakeRequests[0].respondWith({ status: 200, responseText: @courseInstances.stringify() })
|
||||||
|
@view.students.fakeRequests[0].respondWith({ status: 200, responseText: @students.stringify() })
|
||||||
|
@view.classroom.sessions.fakeRequests[0].respondWith({ status: 200, responseText: @levelSessions.stringify() })
|
||||||
|
@view.levels.fakeRequests[0].respondWith({ status: 200, responseText: @levels.stringify() })
|
||||||
|
|
||||||
|
jasmine.demoEl(@view.$el)
|
||||||
|
_.defer done
|
||||||
|
|
||||||
describe 'Export Student Progress (CSV) button', ->
|
describe 'Export Student Progress (CSV) button', ->
|
||||||
it 'downloads a CSV file', ->
|
it 'downloads a CSV file', ->
|
||||||
spyOn(window, 'open').and.callFake (encodedCSV) =>
|
spyOn(window, 'open').and.callFake (encodedCSV) =>
|
||||||
|
|
Reference in a new issue