Add completion stats to /courses
This commit is contained in:
parent
07ad8382cc
commit
405785bafe
4 changed files with 64 additions and 25 deletions
app
models
styles/courses
templates/courses
views/courses
|
@ -11,5 +11,26 @@ module.exports = class Campaign extends CocoModel
|
||||||
@denormalizedLevelProperties: _.keys(_.omit(schema.properties.levels.additionalProperties.properties, ['unlocks', 'position', 'rewards']))
|
@denormalizedLevelProperties: _.keys(_.omit(schema.properties.levels.additionalProperties.properties, ['unlocks', 'position', 'rewards']))
|
||||||
@denormalizedCampaignProperties: ['name', 'i18n', 'slug']
|
@denormalizedCampaignProperties: ['name', 'i18n', 'slug']
|
||||||
|
|
||||||
levelsCollection: ->
|
statsForSessions: (sessions) ->
|
||||||
new CocoCollection(_.values(@get('levels')), {model: Level})
|
# common code for crunching stats for a user's progress on a campaign/course
|
||||||
|
stats = {}
|
||||||
|
sessions = _.sortBy sessions.models, (s) -> s.get('changed')
|
||||||
|
levels = _.values(@get('levels'))
|
||||||
|
levels = (level for level in levels when not _.contains(level.type, 'ladder'))
|
||||||
|
levelOriginals = _.pluck(levels, 'original')
|
||||||
|
sessionOriginals = (session.get('level').original for session in sessions when session.get('state').complete)
|
||||||
|
levelsLeft = _.size(_.difference(levelOriginals, sessionOriginals))
|
||||||
|
lastSession = _.last(sessions)
|
||||||
|
stats.levels = {
|
||||||
|
size: _.size(levels)
|
||||||
|
left: levelsLeft
|
||||||
|
done: levelsLeft is 0
|
||||||
|
numDone: _.size(levels) - levelsLeft
|
||||||
|
pctDone: (100 * (_.size(levels) - levelsLeft) / _.size(levels)).toFixed(1) + '%'
|
||||||
|
lastPlayed: if lastSession then _.findWhere levels, { original: lastSession.get('level').original } else null
|
||||||
|
first: _.first(levels)
|
||||||
|
arena: _.find _.values(@get('levels')), (level) -> _.contains(level.type, 'ladder')
|
||||||
|
}
|
||||||
|
sum = (nums) -> _.reduce(nums, (s, num) -> s + num)
|
||||||
|
stats.playtime = sum((session.get('playtime') or 0 for session in sessions))
|
||||||
|
return stats
|
|
@ -29,3 +29,11 @@
|
||||||
|
|
||||||
.course-instance-entry
|
.course-instance-entry
|
||||||
padding-left: 40px
|
padding-left: 40px
|
||||||
|
|
||||||
|
.progress-bar
|
||||||
|
min-width: 15%
|
||||||
|
|
||||||
|
.btn
|
||||||
|
margin-left: 20px
|
||||||
|
min-width: 180px
|
||||||
|
|
|
@ -28,8 +28,11 @@ block content
|
||||||
else
|
else
|
||||||
|
|
||||||
- var showHOCComplete = false;
|
- var showHOCComplete = false;
|
||||||
if false && view.hocCourseInstance
|
if view.hocCourseInstance
|
||||||
- showHOCComplete = view.hocCourseInstance.sessions.allDone();
|
- var course = view.courses.get(view.hocCourseInstance.get('courseID'));
|
||||||
|
- var campaign = view.campaigns.get(course.get('campaignID'));
|
||||||
|
- var stats = campaign.statsForSessions(view.hocCourseInstance.sessions);
|
||||||
|
- showHOCComplete = stats.levels.done;
|
||||||
|
|
||||||
.text-center
|
.text-center
|
||||||
if !showHOCComplete
|
if !showHOCComplete
|
||||||
|
@ -43,7 +46,7 @@ block content
|
||||||
li Learn even more programming!
|
li Learn even more programming!
|
||||||
a.btn.btn-lg.btn-success(href="/play")
|
a.btn.btn-lg.btn-success(href="/play")
|
||||||
|
|
||||||
if false && view.hocCourseInstance
|
if view.hocCourseInstance && !view.classrooms.size()
|
||||||
h3 Saved Games
|
h3 Saved Games
|
||||||
hr
|
hr
|
||||||
|
|
||||||
|
@ -56,7 +59,7 @@ block content
|
||||||
+course-instance-body(view.hocCourseInstance)
|
+course-instance-body(view.hocCourseInstance)
|
||||||
.clearfix
|
.clearfix
|
||||||
|
|
||||||
if view.classrooms.size()
|
else if view.classrooms.size()
|
||||||
h3.text-uppercase My Classes
|
h3.text-uppercase My Classes
|
||||||
hr
|
hr
|
||||||
|
|
||||||
|
@ -111,29 +114,43 @@ block content
|
||||||
mixin course-instance-body(courseInstance)
|
mixin course-instance-body(courseInstance)
|
||||||
- var course = view.courses.get(courseInstance.get('courseID'));
|
- var course = view.courses.get(courseInstance.get('courseID'));
|
||||||
- var campaign = view.campaigns.get(course.get('campaignID'));
|
- var campaign = view.campaigns.get(course.get('campaignID'));
|
||||||
- var levels = campaign.levelsCollection();
|
- var stats = campaign.statsForSessions(courseInstance.sessions);
|
||||||
- var complete = view.isCampaignComplete(campaign, courseInstance.sessions);
|
if stats.levels.done
|
||||||
if complete
|
|
||||||
.text-success
|
.text-success
|
||||||
span.glyphicon.glyphicon-ok
|
span.glyphicon.glyphicon-ok
|
||||||
span.spl Course Complete!
|
span.spl Course Complete!
|
||||||
.pull-right
|
.pull-right
|
||||||
if complete
|
if stats.levels.done
|
||||||
- var arenaLevel = levels.findWhere({ type: 'course-ladder' });
|
- var arenaLevel = stats.levels.arena;
|
||||||
if arenaLevel
|
if arenaLevel
|
||||||
- var arenaURL = "/play/ladder/"+arenaLevel.get('slug')+"/course/"+courseInstance.id;
|
- var arenaURL = "/play/ladder/"+arenaLevel.slug+"/course/"+courseInstance.id;
|
||||||
a.btn.btn-warning.btn-lg(href=arenaURL)
|
a.btn.btn-warning.btn-lg(href=arenaURL)
|
||||||
| Play Arena
|
| Play Arena
|
||||||
else
|
else
|
||||||
a.btn.btn-default.btn-lg(disabled=true) Course Complete
|
a.btn.btn-default.btn-lg(disabled=true) Course Complete
|
||||||
else if courseInstance.sessions.size()
|
else if courseInstance.sessions.size()
|
||||||
- var lastSession = courseInstance.sessions.last();
|
- var lastLevel = stats.levels.lastPlayed;
|
||||||
- var lastLevel = levels.findWhere({original: lastSession.get('level').original});
|
- var levelURL = "/play/level/"+lastLevel.slug+"?course="+courseInstance.get('courseID')+"&course-instance="+courseInstance.id;
|
||||||
- var levelURL = "/play/level/"+lastLevel.get('slug')+"?course="+courseInstance.get('courseID')+"&course-instance="+courseInstance.id;
|
|
||||||
a.btn.btn-success.btn-lg(href=levelURL)
|
a.btn.btn-success.btn-lg(href=levelURL)
|
||||||
| Continue
|
| Continue
|
||||||
else
|
else
|
||||||
- var firstLevel = levels.first();
|
- var firstLevel = stats.levels.first;
|
||||||
- var levelURL = "/play/level/"+firstLevel.get('slug')+"?course="+courseInstance.get('courseID')+"&course-instance="+courseInstance.id;
|
- var levelURL = "/play/level/"+firstLevel.slug+"?course="+courseInstance.get('courseID')+"&course-instance="+courseInstance.id;
|
||||||
a.btn.btn-info.btn-lg(href=levelURL)
|
a.btn.btn-info.btn-lg(href=levelURL)
|
||||||
| Start
|
| Start
|
||||||
|
|
||||||
|
div
|
||||||
|
span Playtime
|
||||||
|
span.spr :
|
||||||
|
span= moment.duration(stats.playtime, 'seconds').humanize()
|
||||||
|
|
||||||
|
if stats.levels.lastPlayed
|
||||||
|
div
|
||||||
|
span Last Level
|
||||||
|
span.spr :
|
||||||
|
span= stats.levels.lastPlayed.name
|
||||||
|
|
||||||
|
.progress
|
||||||
|
.progress-bar(style="width:"+stats.levels.pctDone)= stats.levels.pctDone
|
||||||
|
|
||||||
|
|
|
@ -49,19 +49,12 @@ module.exports = class CoursesView extends RootView
|
||||||
model: LevelSession
|
model: LevelSession
|
||||||
})
|
})
|
||||||
courseInstance.sessions.comparator = 'changed'
|
courseInstance.sessions.comparator = 'changed'
|
||||||
@supermodel.loadCollection(courseInstance.sessions, 'sessions', { data: { project: 'state.complete level.original' }})
|
@supermodel.loadCollection(courseInstance.sessions, 'sessions', { data: { project: 'state.complete level.original playtime changed' }})
|
||||||
|
|
||||||
@hocCourseInstance = @courseInstances.findWhere({hourOfCode: true})
|
@hocCourseInstance = @courseInstances.findWhere({hourOfCode: true})
|
||||||
if @hocCourseInstance
|
if @hocCourseInstance
|
||||||
@courseInstances.remove(@hocCourseInstance)
|
@courseInstances.remove(@hocCourseInstance)
|
||||||
|
|
||||||
isCampaignComplete: (campaign, sessions) ->
|
|
||||||
levels = _.values(campaign.get('levels'))
|
|
||||||
levels = (level for level in levels when not _.contains(level.type, 'ladder'))
|
|
||||||
levelOriginals = _.pluck(levels, 'original')
|
|
||||||
sessionOriginals = (session.get('level').original for session in sessions.models)
|
|
||||||
return _.size(_.difference(levelOriginals, sessionOriginals)) is 0
|
|
||||||
|
|
||||||
onClickStartNewGameButton: ->
|
onClickStartNewGameButton: ->
|
||||||
@openSignUpModal()
|
@openSignUpModal()
|
||||||
|
|
||||||
|
|
Reference in a new issue