learning mastery student view: basic info and progress bars

fixes CNVS-12922
fixes CNVS-11552

test plan:
  * enable learning mastery student view
  * open a student's grades page
  * open the learning mastery tab
  * verify that outcome groups have the correct statuses
  * verify that the outcome have the correct statuses
  * verify that the progress bars are correctly color coded
    and in the correct percentages

Change-Id: I2aa0a73d4a831d9795308efc10cdf13123fe7dce
Reviewed-on: https://gerrit.instructure.com/35844
Reviewed-by: Braden Anderson <banderson@instructure.com>
Tested-by: Jenkins <jenkins@instructure.com>
QA-Review: Steven Shepherd <sshepherd@instructure.com>
Product-Review: Drew Bowman <dbowman@instructure.com>
This commit is contained in:
Drew Bowman 2014-06-05 22:04:25 -04:00
parent e8168f02c5
commit 66ab0c0b9b
14 changed files with 738 additions and 398 deletions

View File

@ -9,10 +9,38 @@ define [
count: -> @get('outcomes').length
mastery_count: ->
statusCount: (status) ->
@get('outcomes').filter((x) ->
x.status() == 'mastery'
x.status() == status
).length
mastery_count: ->
@statusCount('mastery')
remedialCount: ->
@statusCount('remedial')
undefinedCount: ->
@statusCount('undefined')
status: ->
if @remedialCount() > 0
"remedial"
else
if @mastery_count() == @count()
"mastery"
else if @undefinedCount() == @count()
"undefined"
else
"near"
started: ->
true
toJSON: ->
_.extend(super, count: @count(), mastery_count: @mastery_count())
_.extend super,
count: @count()
mastery_count: @mastery_count()
started: @started()
status: @status()

View File

@ -4,14 +4,33 @@ define [
], (_, {Model, Collection}) ->
class Outcome extends Model
status: ->
score = @get('score')
mastery = @get('mastery_points')
if score >= mastery
'mastery'
else if score >= mastery / 2
'near'
if @scoreDefined()
score = @get('score')
mastery = @get('mastery_points')
if score >= mastery
'mastery'
else if score >= mastery / 2
'near'
else
'remedial'
else
'remedial'
'undefined'
scoreDefined: ->
_.isNumber(@get('score'))
percentProgress: ->
if @scoreDefined()
@get('score')/@get('points_possible') * 100
else
0
masteryPercent: ->
@get('mastery_points')/@get('points_possible') * 100
toJSON: ->
_.extend(super, status: @status())
_.extend super,
status: @status()
scoreDefined: @scoreDefined()
percentProgress: @percentProgress()
masteryPercent: @masteryPercent()

View File

@ -1,10 +1,11 @@
define [
'i18n!outcomes'
'Backbone'
'underscore'
'compiled/views/CollectionView'
'compiled/views/grade_summary/OutcomeView'
'jst/grade_summary/group'
], ({View, Collection}, _, CollectionView, OutcomeView, template) ->
], (I18n, {View, Collection}, _, CollectionView, OutcomeView, template) ->
class GroupView extends View
tagName: 'li'
@ -22,3 +23,15 @@ define [
collection: @model.get('outcomes')
itemView: OutcomeView
outcomesView.render()
statusTooltip: ->
switch @model.status()
when 'undefined' then I18n.t 'undefined', 'Unstarted'
when 'remedial' then I18n.t 'remedial', 'Remedial'
when 'near' then I18n.t 'near', 'Near mastery'
when 'mastery' then I18n.t 'mastery', 'Mastery'
toJSON: ->
json = super
_.extend json,
statusTooltip: @statusTooltip()

View File

@ -1,12 +1,22 @@
define [
'i18n!outcomes'
'underscore'
'Backbone'
'jst/grade_summary/outcome'
], (_, Backbone, template) ->
], (I18n, _, Backbone, template) ->
class OutcomeView extends Backbone.View
tagName: 'li'
className: 'outcome'
template: template
statusTooltip: ->
switch @model.status()
when 'undefined' then I18n.t 'undefined', 'Unstarted'
when 'remedial' then I18n.t 'remedial', 'Remedial'
when 'near' then I18n.t 'near', 'Near mastery'
when 'mastery' then I18n.t 'mastery', 'Mastery'
toJSON: ->
json = super
_.extend({score_defined: _.isNumber(json.score)}, json)
_.extend json,
statusTooltip: @statusTooltip()

View File

@ -1,7 +1,7 @@
// print button for grades summary page
.print-grades
float: right
@media print
display: none
@ -9,7 +9,7 @@
#grades_summary
border-collapse: collapse
border-bottom: 1px solid #eee
@media print
border-bottom: none
a
@ -24,7 +24,7 @@
background-color: #444
color: #fff
text-align: left
@media print
:text-transform uppercase
:color black
@ -34,13 +34,13 @@
:padding-left 0
#grades_summary tr
> *
padding: 5px
text-align: left
border-top: 2px solid black
background-color: #fff
@media print
border-top: 1px solid black
color: black !important
@ -50,7 +50,7 @@
border-width: 0
font-size: 0.8em
padding-left: 30px
@media print
padding-left: 0
font-style: italic
@ -71,7 +71,7 @@
#grades_summary tr.student_assignment.dropped td
color: #ccc
@media print
color: black
@ -82,7 +82,7 @@
#grades_summary tr.student_assignment.dropped td a
color: #aaa
@media print
color: black
font-weight: normal
@ -121,7 +121,7 @@
font-weight: normal
padding-left: 20px
word-break: break-all
@media print
padding-left: 0
font-size: 1.0em
@ -133,7 +133,7 @@
color: #999
padding-left: 10px
line-height: 0.5em
@media print
visibility: visible
margin: 5px 0
@ -145,16 +145,16 @@
#grades_summary td.due
font-size: 0.8em
white-space: nowrap
@media print
font-size: 1em
#grades_summary tr.assignment_graded > *
background-color: #eee
@media print
background-color: transparent
&.title
font-weight: bold
@ -163,7 +163,7 @@
color: #eee
font-size: 1.5em
font-weight: bold
@media print
background-color: transparent
text-transform: uppercase
@ -175,12 +175,12 @@
background-color: #ccc
border-top-width: 0
font-weight: bold
@media print
background-color: transparent
border-top-width: 1px
text-transform: uppercase
&.title .context
display: none
@ -312,9 +312,35 @@ $outcome-border: 1px solid #BCC2CA
.group-description
overflow: hidden
.status
padding: 10px 50px 10px 15px
.outcome-icon i
line-height: 40px
.group-title
margin: 0
font-size: 20px
line-height: 40px
.group-status
float: right
margin: 1em
margin: 0
text-align: center
font-size: 30px
color: #959595
position: relative
top: 4px
strong
color: #2a333b
span
display: block
font-size: 14px
.undefined>i
color: #C0C0C0
.remedial>i
color: #F76A66
.near>i
color: #DFB056
.mastery>i
color: #5DC28F
.outcomes
background-color: #f7f7f7
@ -326,23 +352,58 @@ $outcome-border: 1px solid #BCC2CA
list-style-type: none
overflow: hidden
.outcome-icon
float: left
margin-right: 15px
li.outcome
padding: 20px 50px 20px 15px
border-top: $outcome-border
.outcome-properties
float: left
width: 80%
padding: 1em
box-sizing: border-box
width: 70%
.description
color: #b4b4b4
color: #2a333b
p
margin: 0
.title
font-weight: bold
.score
.outcome-score
float: right
width: 20%
padding: 1em
box-sizing: border-box
width: 165px
padding-top: 10px
.bar
background: #e1e1e1
height: 8px
position: relative
.bar-marker
position: absolute
width: 3px
background: #f7f7f7
z-index: 5
height: 8px
top: 0
.bar-progress
height: 8px
&.remedial
background: #F76A66
&.near
background: #DFB056
&.mastery
background: #5DC28F
.score
color: #959595
font-size: 18px
float: left
strong
color: #2a333b
a
float: right
line-height: 22px

View File

@ -356,7 +356,12 @@ h1, h2, h3, h4, .h1, .h2, .h3, .h4, p {
// define specific icon classes, mapped to the "canvasregular" icon font above
.icon-trouble:before { content: "\e600"; }
.icon-partial:before { content: "\e601"; }
.icon-expand:before { content: "\e602"; }
.icon-empty:before { content: "\e603"; }
.icon-complete:before { content: "\e604"; }
.icon-collapse:before { content: "\e605"; }
.icon-stats:before { content: "\e06a" }
.icon-paperclip:before { content: "\e03d" }
.icon-media:before { content: "\e062" }

View File

@ -1,8 +1,23 @@
<div class="group-description">
<h3>{{title}}</h3>
<div class="status">
{{mastery_count}} / {{count}}
</div>
<div class="outcome-icon {{status}}" data-tooltip title="{{statusTooltip}}" aria-label="Outcome Group: {{title}} status is {{statusTooltip}}">
{{#if started}}
{{#ifEqual status 'mastery'}}
<i class="icon-complete"></i>
{{else}}
{{#ifEqual status 'near'}}
<i class="icon-partial"></i>
{{else}}
<i class="icon-trouble"></i>
{{/ifEqual}}
{{/ifEqual}}
{{else}}
<i class="icon-empty"></i>
{{/if}}
</div>
<h3 class="group-title">{{title}}</h3>
<div class="group-status" aria-label="{{mastery_count}} out of {{count}} outcomes mastered">
<strong>{{mastery_count}}</strong>/{{count}} <span>Outcomes</span>
</div>
</div>
<div class="outcomes">
</div>

View File

@ -1,7 +1,29 @@
<div class="outcome-icon {{status}}" data-tooltip="top" title="{{statusTooltip}}" aria-label="Outcome: {{title}} has a status of {{statusTooltip}}">
{{#if scoreDefined}}
{{#ifEqual status 'mastery'}}
{{addIcon 'complete'}}
{{else}}
{{#ifEqual status 'near'}}
{{addIcon 'partial'}}
{{else}}
{{addIcon 'trouble'}}
{{/ifEqual}}
{{/ifEqual}}
{{else}}
<i class="icon-empty"></i>
{{/if}}
</div>
<div class="outcome-properties">
<div class="title">{{title}}</div>
<div class="title" data-tooltip title="{{description}}">{{title}}</div>
<div class="description">{{{description}}}</div>
</div>
<div class="score">
{{#if score_defined}}{{score}}{{else}}-{{/if}}/{{mastery_points}}
<div class="outcome-score">
<div class="bar">
<div class="bar-marker" style="left: {{masteryPercent}}%"></div>
<div class="bar-progress {{status}}" style="width: {{percentProgress}}%"></div>
</div>
<span class="score" aria-label="Score: {{score}} out of {{points_possible}} points with mastery at {{mastery_points}}">
<strong>{{#if scoreDefined}}{{score}}{{else}}-{{/if}}</strong>/{{mastery_points}}
</span>
<a href="#"><strong>X</strong> Alignments</a>
</div>

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 109 KiB

View File

@ -0,0 +1,38 @@
define [
'compiled/models/grade_summary/Outcome'
], (Outcome) ->
module "Outcome"
test "#status should be mastery if the score equals the mastery points", ->
outcome = new Outcome({score: 3, mastery_points: 3})
equal outcome.status(), 'mastery'
test "#status should be mastery if the score is greater than the mastery points", ->
outcome = new Outcome({score: 4, mastery_points: 3})
equal outcome.status(), 'mastery'
test "#status should be near if the score is greater than half the mastery points", ->
outcome = new Outcome({score: 2, mastery_points: 3})
equal outcome.status(), 'near'
test "#status should be remedial if the score is less than half the mastery points", ->
outcome = new Outcome({score: 1, mastery_points: 3})
equal outcome.status(), 'remedial'
test "#status should be undefined if there is no score", ->
outcome = new Outcome({mastery_points: 3})
equal outcome.status(), 'undefined'
test "#percentProgress should be zero if score isn't defined", ->
outcome = new Outcome({points_possible: 3})
equal outcome.percentProgress(), 0
test "#percentProgress should be score over points possible", ->
outcome = new Outcome({score: 5, points_possible: 10})
equal outcome.percentProgress(), 50
test "#masteryPercent should be master_points over points possible", ->
outcome = new Outcome({mastery_points: 5, points_possible: 10})
equal outcome.masteryPercent(), 50