Add completion stats to /courses

This commit is contained in:
Scott Erickson 2015-11-29 17:18:24 -05:00
parent 07ad8382cc
commit 405785bafe
4 changed files with 64 additions and 25 deletions
app
models
styles/courses
templates/courses
views/courses

View file

@ -11,5 +11,26 @@ module.exports = class Campaign extends CocoModel
@denormalizedLevelProperties: _.keys(_.omit(schema.properties.levels.additionalProperties.properties, ['unlocks', 'position', 'rewards']))
@denormalizedCampaignProperties: ['name', 'i18n', 'slug']
levelsCollection: ->
new CocoCollection(_.values(@get('levels')), {model: Level})
statsForSessions: (sessions) ->
# 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

View file

@ -29,3 +29,11 @@
.course-instance-entry
padding-left: 40px
.progress-bar
min-width: 15%
.btn
margin-left: 20px
min-width: 180px

View file

@ -28,8 +28,11 @@ block content
else
- var showHOCComplete = false;
if false && view.hocCourseInstance
- showHOCComplete = view.hocCourseInstance.sessions.allDone();
if view.hocCourseInstance
- 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
if !showHOCComplete
@ -43,7 +46,7 @@ block content
li Learn even more programming!
a.btn.btn-lg.btn-success(href="/play")
if false && view.hocCourseInstance
if view.hocCourseInstance && !view.classrooms.size()
h3 Saved Games
hr
@ -56,7 +59,7 @@ block content
+course-instance-body(view.hocCourseInstance)
.clearfix
if view.classrooms.size()
else if view.classrooms.size()
h3.text-uppercase My Classes
hr
@ -111,29 +114,43 @@ block content
mixin course-instance-body(courseInstance)
- var course = view.courses.get(courseInstance.get('courseID'));
- var campaign = view.campaigns.get(course.get('campaignID'));
- var levels = campaign.levelsCollection();
- var complete = view.isCampaignComplete(campaign, courseInstance.sessions);
if complete
- var stats = campaign.statsForSessions(courseInstance.sessions);
if stats.levels.done
.text-success
span.glyphicon.glyphicon-ok
span.spl Course Complete!
.pull-right
if complete
- var arenaLevel = levels.findWhere({ type: 'course-ladder' });
if stats.levels.done
- var arenaLevel = stats.levels.arena;
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)
| Play Arena
else
a.btn.btn-default.btn-lg(disabled=true) Course Complete
else if courseInstance.sessions.size()
- var lastSession = courseInstance.sessions.last();
- var lastLevel = levels.findWhere({original: lastSession.get('level').original});
- var levelURL = "/play/level/"+lastLevel.get('slug')+"?course="+courseInstance.get('courseID')+"&course-instance="+courseInstance.id;
- var lastLevel = stats.levels.lastPlayed;
- var levelURL = "/play/level/"+lastLevel.slug+"?course="+courseInstance.get('courseID')+"&course-instance="+courseInstance.id;
a.btn.btn-success.btn-lg(href=levelURL)
| Continue
else
- var firstLevel = levels.first();
- var levelURL = "/play/level/"+firstLevel.get('slug')+"?course="+courseInstance.get('courseID')+"&course-instance="+courseInstance.id;
- var firstLevel = stats.levels.first;
- var levelURL = "/play/level/"+firstLevel.slug+"?course="+courseInstance.get('courseID')+"&course-instance="+courseInstance.id;
a.btn.btn-info.btn-lg(href=levelURL)
| 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

View file

@ -49,19 +49,12 @@ module.exports = class CoursesView extends RootView
model: LevelSession
})
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})
if @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: ->
@openSignUpModal()