Decaffeinate ui/features/grade_summary
Refs FOO-3200 flag=none Remove all CoffeeScript from this feature bundle. Test plan: * all tests pass Change-Id: I2d0e495699931a9963c49816d2adcffec5e92142 Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/304696 Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> Reviewed-by: Cameron Ray <cameron.ray@instructure.com> QA-Review: Cameron Ray <cameron.ray@instructure.com> Product-Review: Charley Kline <ckline@instructure.com>
This commit is contained in:
parent
03ffd9d120
commit
c08fe4afdc
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
import Backbone from '@canvas/backbone'
|
import Backbone from '@canvas/backbone'
|
||||||
import Outcome from '@canvas/grade-summary/backbone/models/Outcome.coffee'
|
import Outcome from '@canvas/grade-summary/backbone/models/Outcome.coffee'
|
||||||
import OutcomeResultCollection from 'ui/features/grade_summary/backbone/collections/OutcomeResultCollection.coffee'
|
import OutcomeResultCollection from 'ui/features/grade_summary/backbone/collections/OutcomeResultCollection'
|
||||||
import fakeENV from 'helpers/fakeENV'
|
import fakeENV from 'helpers/fakeENV'
|
||||||
import tz from '@canvas/timezone'
|
import tz from '@canvas/timezone'
|
||||||
|
|
||||||
|
@ -96,6 +96,7 @@ QUnit.module('OutcomeResultCollectionSpec', {
|
||||||
})
|
})
|
||||||
|
|
||||||
test('default params reflect aligned outcome', function () {
|
test('default params reflect aligned outcome', function () {
|
||||||
|
// eslint-disable-next-line new-cap
|
||||||
const collectionModel = new this.outcomeResultCollection.model()
|
const collectionModel = new this.outcomeResultCollection.model()
|
||||||
deepEqual(collectionModel.get('mastery_points'), 8)
|
deepEqual(collectionModel.get('mastery_points'), 8)
|
||||||
deepEqual(collectionModel.get('points_possible'), 10)
|
deepEqual(collectionModel.get('points_possible'), 10)
|
||||||
|
|
|
@ -18,9 +18,9 @@
|
||||||
|
|
||||||
import Backbone from '@canvas/backbone'
|
import Backbone from '@canvas/backbone'
|
||||||
import CollectionView from '@canvas/backbone-collection-view'
|
import CollectionView from '@canvas/backbone-collection-view'
|
||||||
import OutcomeResultCollection from 'ui/features/grade_summary/backbone/collections/OutcomeResultCollection.coffee'
|
import OutcomeResultCollection from 'ui/features/grade_summary/backbone/collections/OutcomeResultCollection'
|
||||||
import Outcome from '@canvas/grade-summary/backbone/models/Outcome.coffee'
|
import Outcome from '@canvas/grade-summary/backbone/models/Outcome.coffee'
|
||||||
import Group from 'ui/features/grade_summary/backbone/models/Group.coffee'
|
import Group from 'ui/features/grade_summary/backbone/models/Group'
|
||||||
import OutcomeDetailView from 'ui/features/grade_summary/backbone/views/OutcomeDetailView'
|
import OutcomeDetailView from 'ui/features/grade_summary/backbone/views/OutcomeDetailView'
|
||||||
import fakeENV from 'helpers/fakeENV'
|
import fakeENV from 'helpers/fakeENV'
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,11 @@
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import $ from 'jquery'
|
||||||
import _ from 'underscore'
|
import _ from 'underscore'
|
||||||
import Outcome from '@canvas/grade-summary/backbone/models/Outcome.coffee'
|
import Outcome from '@canvas/grade-summary/backbone/models/Outcome.coffee'
|
||||||
import OutcomeDialogView from 'ui/features/grade_summary/backbone/views/OutcomeDialogView.coffee'
|
import OutcomeDialogView from 'ui/features/grade_summary/backbone/views/OutcomeDialogView'
|
||||||
import OutcomeLineGraphView from 'ui/features/grade_summary/backbone/views/OutcomeLineGraphView.coffee'
|
import OutcomeLineGraphView from 'ui/features/grade_summary/backbone/views/OutcomeLineGraphView'
|
||||||
|
|
||||||
QUnit.module('OutcomeDialogViewSpec', {
|
QUnit.module('OutcomeDialogViewSpec', {
|
||||||
setup() {
|
setup() {
|
||||||
|
|
|
@ -16,10 +16,11 @@
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import $ from 'jquery'
|
||||||
import {isUndefined} from 'lodash'
|
import {isUndefined} from 'lodash'
|
||||||
import Outcome from '@canvas/grade-summary/backbone/models/Outcome.coffee'
|
import Outcome from '@canvas/grade-summary/backbone/models/Outcome.coffee'
|
||||||
import OutcomeResultCollection from 'ui/features/grade_summary/backbone/collections/OutcomeResultCollection.coffee'
|
import OutcomeResultCollection from 'ui/features/grade_summary/backbone/collections/OutcomeResultCollection'
|
||||||
import OutcomeLineGraphView from 'ui/features/grade_summary/backbone/views/OutcomeLineGraphView.coffee'
|
import OutcomeLineGraphView from 'ui/features/grade_summary/backbone/views/OutcomeLineGraphView'
|
||||||
import tz from '@canvas/timezone'
|
import tz from '@canvas/timezone'
|
||||||
import fakeENV from 'helpers/fakeENV'
|
import fakeENV from 'helpers/fakeENV'
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ import $ from 'jquery'
|
||||||
import {isUndefined} from 'lodash'
|
import {isUndefined} from 'lodash'
|
||||||
import Popover from 'jquery-popover'
|
import Popover from 'jquery-popover'
|
||||||
import Outcome from '@canvas/grade-summary/backbone/models/Outcome.coffee'
|
import Outcome from '@canvas/grade-summary/backbone/models/Outcome.coffee'
|
||||||
import OutcomePopoverView from 'ui/features/grade_summary/backbone/views/OutcomePopoverView.coffee'
|
import OutcomePopoverView from 'ui/features/grade_summary/backbone/views/OutcomePopoverView'
|
||||||
import template from '@canvas/outcomes/jst/outcomePopover.handlebars'
|
import template from '@canvas/outcomes/jst/outcomePopover.handlebars'
|
||||||
|
|
||||||
QUnit.module('OutcomePopoverViewSpec', {
|
QUnit.module('OutcomePopoverViewSpec', {
|
||||||
|
@ -42,7 +42,7 @@ QUnit.module('OutcomePopoverViewSpec', {
|
||||||
})
|
})
|
||||||
|
|
||||||
test('closePopover', function () {
|
test('closePopover', function () {
|
||||||
ok(isUndefined(this.popoverView.popover, 'precondition'))
|
ok(isUndefined(this.popoverView.popover), 'precondition')
|
||||||
ok(this.popoverView.closePopover())
|
ok(this.popoverView.closePopover())
|
||||||
this.popoverView.popover = new Popover(this.e('mouseleave'), this.popoverView.render(), {
|
this.popoverView.popover = new Popover(this.e('mouseleave'), this.popoverView.render(), {
|
||||||
verticalSide: 'bottom',
|
verticalSide: 'bottom',
|
||||||
|
|
|
@ -16,12 +16,13 @@
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import $ from 'jquery'
|
||||||
import {isUndefined} from 'lodash'
|
import {isUndefined} from 'lodash'
|
||||||
import Outcome from '@canvas/grade-summary/backbone/models/Outcome.coffee'
|
import Outcome from '@canvas/grade-summary/backbone/models/Outcome.coffee'
|
||||||
import OutcomePopoverView from 'ui/features/grade_summary/backbone/views/OutcomePopoverView.coffee'
|
import OutcomePopoverView from 'ui/features/grade_summary/backbone/views/OutcomePopoverView'
|
||||||
import OutcomeDialogView from 'ui/features/grade_summary/backbone/views/OutcomeDialogView.coffee'
|
import OutcomeDialogView from 'ui/features/grade_summary/backbone/views/OutcomeDialogView'
|
||||||
import OutcomeView from 'ui/features/grade_summary/backbone/views/OutcomeView.coffee'
|
import OutcomeView from 'ui/features/grade_summary/backbone/views/OutcomeView'
|
||||||
import ProgressBarView from 'ui/features/grade_summary/backbone/views/ProgressBarView.coffee'
|
import ProgressBarView from 'ui/features/grade_summary/backbone/views/ProgressBarView'
|
||||||
import assertions from 'helpers/assertions'
|
import assertions from 'helpers/assertions'
|
||||||
|
|
||||||
QUnit.module('OutcomeViewSpec', {
|
QUnit.module('OutcomeViewSpec', {
|
||||||
|
@ -36,6 +37,7 @@ QUnit.module('OutcomeViewSpec', {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// eslint-disable-next-line qunit/resolve-async
|
||||||
test('should be accessible', function (assert) {
|
test('should be accessible', function (assert) {
|
||||||
const done = assert.async()
|
const done = assert.async()
|
||||||
assertions.isAccessible(this.outcomeView, done, {a11yReport: true})
|
assertions.isAccessible(this.outcomeView, done, {a11yReport: true})
|
||||||
|
@ -46,7 +48,7 @@ test('assign instance of ProgressBarView on init', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
test('have after render behavior', function () {
|
test('have after render behavior', function () {
|
||||||
ok(isUndefined(this.outcomeView.popover, 'precondition'))
|
ok(isUndefined(this.outcomeView.popover), 'precondition')
|
||||||
this.outcomeView.render()
|
this.outcomeView.render()
|
||||||
ok(this.outcomeView.popover instanceof OutcomePopoverView)
|
ok(this.outcomeView.popover instanceof OutcomePopoverView)
|
||||||
ok(this.outcomeView.dialog instanceof OutcomeDialogView)
|
ok(this.outcomeView.dialog instanceof OutcomeDialogView)
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
#
|
|
||||||
# Copyright (C) 2015 - present Instructure, Inc.
|
|
||||||
#
|
|
||||||
# This file is part of Canvas.
|
|
||||||
#
|
|
||||||
# Canvas is free software: you can redistribute it and/or modify it under
|
|
||||||
# the terms of the GNU Affero General Public License as published by the Free
|
|
||||||
# Software Foundation, version 3 of the License.
|
|
||||||
#
|
|
||||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
||||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
||||||
# details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Affero General Public License along
|
|
||||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import Backbone from '@canvas/backbone'
|
|
||||||
import Outcome from '@canvas/grade-summary/backbone/models/Outcome.coffee'
|
|
||||||
import WrappedCollection from './WrappedCollection.coffee'
|
|
||||||
|
|
||||||
export default class OutcomeResultCollection extends WrappedCollection
|
|
||||||
key: 'outcome_results'
|
|
||||||
model: Outcome
|
|
||||||
@optionProperty 'outcome'
|
|
||||||
url: -> "/api/v1/courses/#{@course_id}/outcome_results?user_ids[]=#{@user_id}&outcome_ids[]=#{@outcome.id}&include[]=alignments&per_page=100"
|
|
||||||
loadAll: true
|
|
||||||
|
|
||||||
comparator: (model) ->
|
|
||||||
return -1 * model.get('submitted_or_assessed_at').getTime()
|
|
||||||
|
|
||||||
initialize: ->
|
|
||||||
super
|
|
||||||
@model = Outcome.extend defaults: {
|
|
||||||
points_possible: @outcome.get('points_possible'),
|
|
||||||
mastery_points: @outcome.get('mastery_points')
|
|
||||||
}
|
|
||||||
@course_id = ENV.context_asset_string?.replace('course_', '')
|
|
||||||
@user_id = ENV.student_id
|
|
||||||
@on('reset', @handleReset)
|
|
||||||
@on('add', @handleAdd)
|
|
||||||
|
|
||||||
handleReset: =>
|
|
||||||
@each @handleAdd
|
|
||||||
|
|
||||||
handleAdd: (model) =>
|
|
||||||
alignment_id = model.get('links').alignment
|
|
||||||
model.set('alignment_name', @alignments.get(alignment_id)?.get('name'))
|
|
||||||
if model.get('points_possible') > 0
|
|
||||||
model.set('score', model.get('points_possible') * model.get('percent'))
|
|
||||||
else
|
|
||||||
model.set('score', model.get('mastery_points') * model.get('percent'))
|
|
||||||
|
|
||||||
parse: (response) ->
|
|
||||||
@alignments ?= new Backbone.Collection([])
|
|
||||||
@alignments.add(response?.linked?.alignments || [])
|
|
||||||
response[@key]
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 - present Instructure, Inc.
|
||||||
|
//
|
||||||
|
// This file is part of Canvas.
|
||||||
|
//
|
||||||
|
// Canvas is free software: you can redistribute it and/or modify it under
|
||||||
|
// the terms of the GNU Affero General Public License as published by the Free
|
||||||
|
// Software Foundation, version 3 of the License.
|
||||||
|
//
|
||||||
|
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||||
|
// details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License along
|
||||||
|
// with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import Backbone from '@canvas/backbone'
|
||||||
|
import Outcome from '@canvas/grade-summary/backbone/models/Outcome.coffee'
|
||||||
|
import WrappedCollection from './WrappedCollection'
|
||||||
|
|
||||||
|
class OutcomeResultCollection extends WrappedCollection {
|
||||||
|
constructor(...args) {
|
||||||
|
super(...args)
|
||||||
|
this.handleReset = this.handleReset.bind(this)
|
||||||
|
this.handleAdd = this.handleAdd.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
url = () =>
|
||||||
|
`/api/v1/courses/${this.course_id}/outcome_results?user_ids[]=${this.user_id}&outcome_ids[]=${this.outcome.id}&include[]=alignments&per_page=100`
|
||||||
|
|
||||||
|
comparator = model => -1 * model.get('submitted_or_assessed_at').getTime()
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
super.initialize(...arguments)
|
||||||
|
this.model = Outcome.extend({
|
||||||
|
defaults: {
|
||||||
|
points_possible: this.outcome.get('points_possible'),
|
||||||
|
mastery_points: this.outcome.get('mastery_points'),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
this.course_id = ENV.context_asset_string?.replace('course_', '')
|
||||||
|
this.user_id = ENV.student_id
|
||||||
|
this.on('reset', this.handleReset)
|
||||||
|
this.on('add', this.handleAdd)
|
||||||
|
}
|
||||||
|
|
||||||
|
handleReset = () => this.each(this.handleAdd)
|
||||||
|
|
||||||
|
handleAdd(model) {
|
||||||
|
const alignment_id = model.get('links').alignment
|
||||||
|
model.set('alignment_name', this.alignments.get(alignment_id)?.get('name'))
|
||||||
|
if (model.get('points_possible') > 0) {
|
||||||
|
model.set('score', model.get('points_possible') * model.get('percent'))
|
||||||
|
} else {
|
||||||
|
model.set('score', model.get('mastery_points') * model.get('percent'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parse(response) {
|
||||||
|
if (this.alignments === null || typeof this.alignments === 'undefined') {
|
||||||
|
this.alignments = new Backbone.Collection([])
|
||||||
|
}
|
||||||
|
this.alignments.add(response?.linked?.alignments || [])
|
||||||
|
return response[this.key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OutcomeResultCollection.prototype.key = 'outcome_results'
|
||||||
|
OutcomeResultCollection.prototype.model = Outcome
|
||||||
|
OutcomeResultCollection.optionProperty('outcome')
|
||||||
|
OutcomeResultCollection.prototype.loadAll = true
|
||||||
|
|
||||||
|
export default OutcomeResultCollection
|
|
@ -17,13 +17,12 @@
|
||||||
|
|
||||||
import $ from 'jquery'
|
import $ from 'jquery'
|
||||||
|
|
||||||
import _ from 'underscore'
|
|
||||||
import {Collection} from '@canvas/backbone'
|
import {Collection} from '@canvas/backbone'
|
||||||
import Section from '../models/Section.coffee'
|
import Section from '../models/Section'
|
||||||
import Group from '../models/Group.coffee'
|
import Group from '../models/Group'
|
||||||
import Outcome from '@canvas/grade-summary/backbone/models/Outcome.coffee'
|
import Outcome from '@canvas/grade-summary/backbone/models/Outcome.coffee'
|
||||||
import PaginatedCollection from '@canvas/pagination/backbone/collections/PaginatedCollection.coffee'
|
import PaginatedCollection from '@canvas/pagination/backbone/collections/PaginatedCollection.coffee'
|
||||||
import WrappedCollection from './WrappedCollection.coffee'
|
import WrappedCollection from './WrappedCollection'
|
||||||
import natcompare from '@canvas/util/natcompare'
|
import natcompare from '@canvas/util/natcompare'
|
||||||
|
|
||||||
class GroupCollection extends PaginatedCollection {
|
class GroupCollection extends PaginatedCollection {
|
||||||
|
@ -63,7 +62,7 @@ export default class OutcomeSummaryCollection extends Collection {
|
||||||
|
|
||||||
fetch = () => {
|
fetch = () => {
|
||||||
const dfd = $.Deferred()
|
const dfd = $.Deferred()
|
||||||
const requests = _.values(this.rawCollections).map(collection => {
|
const requests = Object.values(this.rawCollections).map(collection => {
|
||||||
collection.loadAll = true
|
collection.loadAll = true
|
||||||
return collection.fetch()
|
return collection.fetch()
|
||||||
})
|
})
|
||||||
|
@ -73,8 +72,7 @@ export default class OutcomeSummaryCollection extends Collection {
|
||||||
|
|
||||||
rollups() {
|
rollups() {
|
||||||
const studentRollups = this.rawCollections.rollups.at(0).get('scores')
|
const studentRollups = this.rawCollections.rollups.at(0).get('scores')
|
||||||
const pairs = studentRollups.map(x => [x.links.outcome, x])
|
return Object.fromEntries(studentRollups.map(x => [x.links.outcome, x]))
|
||||||
return _.object(pairs)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
populateGroupOutcomes() {
|
populateGroupOutcomes() {
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
#
|
|
||||||
# Copyright (C) 2014 - present Instructure, Inc.
|
|
||||||
#
|
|
||||||
# This file is part of Canvas.
|
|
||||||
#
|
|
||||||
# Canvas is free software: you can redistribute it and/or modify it under
|
|
||||||
# the terms of the GNU Affero General Public License as published by the Free
|
|
||||||
# Software Foundation, version 3 of the License.
|
|
||||||
#
|
|
||||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
||||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
||||||
# details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Affero General Public License along
|
|
||||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import PaginatedCollection from '@canvas/pagination/backbone/collections/PaginatedCollection.coffee'
|
|
||||||
|
|
||||||
export default class WrappedCollection extends PaginatedCollection
|
|
||||||
@optionProperty 'key'
|
|
||||||
|
|
||||||
parse: (response) ->
|
|
||||||
@linked = response.linked
|
|
||||||
response[@key]
|
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// Copyright (C) 2015 - present Instructure, Inc.
|
// Copyright (C) 2014 - present Instructure, Inc.
|
||||||
//
|
//
|
||||||
// This file is part of Canvas.
|
// This file is part of Canvas.
|
||||||
//
|
//
|
||||||
|
@ -15,19 +15,15 @@
|
||||||
// You should have received a copy of the GNU Affero General Public License along
|
// You should have received a copy of the GNU Affero General Public License along
|
||||||
// with this program. If not, see <http://www.gnu.org/licenses/>.
|
// with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
// Adds _.sum method.
|
import PaginatedCollection from '@canvas/pagination/backbone/collections/PaginatedCollection.coffee'
|
||||||
//
|
|
||||||
// Use like:
|
|
||||||
//
|
|
||||||
// _.sum([2,3,4]) #=> 9
|
|
||||||
//
|
|
||||||
// or with a custom accessor:
|
|
||||||
//
|
|
||||||
// _.sum([[2,3], [3,4]], (a) -> a[0]) #=> 5
|
|
||||||
import _ from 'underscore'
|
|
||||||
|
|
||||||
export default _.mixin({
|
class WrappedCollection extends PaginatedCollection {
|
||||||
sum(array, accessor = null, start = 0) {
|
parse(response) {
|
||||||
return _.reduce(array, (memo, el) => (accessor != null ? accessor(el) : el) + memo, start)
|
this.linked = response.linked
|
||||||
},
|
return response[this.key]
|
||||||
})
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WrappedCollection.optionProperty('key')
|
||||||
|
|
||||||
|
export default WrappedCollection
|
|
@ -1,62 +0,0 @@
|
||||||
#
|
|
||||||
# Copyright (C) 2014 - present Instructure, Inc.
|
|
||||||
#
|
|
||||||
# This file is part of Canvas.
|
|
||||||
#
|
|
||||||
# Canvas is free software: you can redistribute it and/or modify it under
|
|
||||||
# the terms of the GNU Affero General Public License as published by the Free
|
|
||||||
# Software Foundation, version 3 of the License.
|
|
||||||
#
|
|
||||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
||||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
||||||
# details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Affero General Public License along
|
|
||||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import _ from 'underscore'
|
|
||||||
import {Model, Collection} from '@canvas/backbone'
|
|
||||||
import natcompare from '@canvas/util/natcompare'
|
|
||||||
|
|
||||||
export default class Group extends Model
|
|
||||||
initialize: ->
|
|
||||||
@set('outcomes', new Collection([], comparator: natcompare.byGet('friendly_name')))
|
|
||||||
|
|
||||||
count: -> @get('outcomes').length
|
|
||||||
|
|
||||||
|
|
||||||
statusCount: (status) ->
|
|
||||||
@get('outcomes').filter((x) ->
|
|
||||||
x.status() == status
|
|
||||||
).length
|
|
||||||
|
|
||||||
mastery_count: ->
|
|
||||||
@statusCount('mastery') + @statusCount('exceeds')
|
|
||||||
|
|
||||||
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()
|
|
||||||
started: @started()
|
|
||||||
status: @status()
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
//
|
||||||
|
// Copyright (C) 2014 - present Instructure, Inc.
|
||||||
|
//
|
||||||
|
// This file is part of Canvas.
|
||||||
|
//
|
||||||
|
// Canvas is free software: you can redistribute it and/or modify it under
|
||||||
|
// the terms of the GNU Affero General Public License as published by the Free
|
||||||
|
// Software Foundation, version 3 of the License.
|
||||||
|
//
|
||||||
|
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||||
|
// details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License along
|
||||||
|
// with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import {Model, Collection} from '@canvas/backbone'
|
||||||
|
import natcompare from '@canvas/util/natcompare'
|
||||||
|
|
||||||
|
export default class Group extends Model {
|
||||||
|
initialize() {
|
||||||
|
return this.set('outcomes', new Collection([], {comparator: natcompare.byGet('friendly_name')}))
|
||||||
|
}
|
||||||
|
|
||||||
|
count() {
|
||||||
|
return this.get('outcomes').length
|
||||||
|
}
|
||||||
|
|
||||||
|
statusCount(status) {
|
||||||
|
return this.get('outcomes').filter(x => x.status() === status).length
|
||||||
|
}
|
||||||
|
|
||||||
|
mastery_count() {
|
||||||
|
return this.statusCount('mastery') + this.statusCount('exceeds')
|
||||||
|
}
|
||||||
|
|
||||||
|
remedialCount() {
|
||||||
|
return this.statusCount('remedial')
|
||||||
|
}
|
||||||
|
|
||||||
|
undefinedCount() {
|
||||||
|
return this.statusCount('undefined')
|
||||||
|
}
|
||||||
|
|
||||||
|
status() {
|
||||||
|
if (this.remedialCount() > 0) {
|
||||||
|
return 'remedial'
|
||||||
|
} else if (this.mastery_count() === this.count()) {
|
||||||
|
return 'mastery'
|
||||||
|
} else if (this.undefinedCount() === this.count()) {
|
||||||
|
return 'undefined'
|
||||||
|
} else {
|
||||||
|
return 'near'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
started() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
return {
|
||||||
|
...super.toJSON(...arguments),
|
||||||
|
count: this.count(),
|
||||||
|
mastery_count: this.mastery_count(),
|
||||||
|
started: this.started(),
|
||||||
|
status: this.status(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,23 +0,0 @@
|
||||||
#
|
|
||||||
# Copyright (C) 2014 - present Instructure, Inc.
|
|
||||||
#
|
|
||||||
# This file is part of Canvas.
|
|
||||||
#
|
|
||||||
# Canvas is free software: you can redistribute it and/or modify it under
|
|
||||||
# the terms of the GNU Affero General Public License as published by the Free
|
|
||||||
# Software Foundation, version 3 of the License.
|
|
||||||
#
|
|
||||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
||||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
||||||
# details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Affero General Public License along
|
|
||||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import {Model, Collection} from '@canvas/backbone'
|
|
||||||
import natcompare from '@canvas/util/natcompare'
|
|
||||||
|
|
||||||
export default class Section extends Model
|
|
||||||
initialize: ->
|
|
||||||
@set('groups', new Collection([], comparator: natcompare.byGet('title')))
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
//
|
||||||
|
// Copyright (C) 2014 - present Instructure, Inc.
|
||||||
|
//
|
||||||
|
// This file is part of Canvas.
|
||||||
|
//
|
||||||
|
// Canvas is free software: you can redistribute it and/or modify it under
|
||||||
|
// the terms of the GNU Affero General Public License as published by the Free
|
||||||
|
// Software Foundation, version 3 of the License.
|
||||||
|
//
|
||||||
|
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||||
|
// details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License along
|
||||||
|
// with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import {Model, Collection} from '@canvas/backbone'
|
||||||
|
import natcompare from '@canvas/util/natcompare'
|
||||||
|
|
||||||
|
export default class Section extends Model {
|
||||||
|
initialize() {
|
||||||
|
return this.set('groups', new Collection([], {comparator: natcompare.byGet('title')}))
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,7 +16,7 @@
|
||||||
// with this program. If not, see <http://www.gnu.org/licenses/>.
|
// with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import Backbone from '@canvas/backbone'
|
import Backbone from '@canvas/backbone'
|
||||||
import ProgressBarView from './ProgressBarView.coffee'
|
import ProgressBarView from './ProgressBarView'
|
||||||
import template from '../../jst/alignment.handlebars'
|
import template from '../../jst/alignment.handlebars'
|
||||||
|
|
||||||
export default class AlignmentView extends Backbone.View {
|
export default class AlignmentView extends Backbone.View {
|
||||||
|
|
|
@ -1,82 +0,0 @@
|
||||||
#
|
|
||||||
# Copyright (C) 2014 - present Instructure, Inc.
|
|
||||||
#
|
|
||||||
# This file is part of Canvas.
|
|
||||||
#
|
|
||||||
# Canvas is free software: you can redistribute it and/or modify it under
|
|
||||||
# the terms of the GNU Affero General Public License as published by the Free
|
|
||||||
# Software Foundation, version 3 of the License.
|
|
||||||
#
|
|
||||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
||||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
||||||
# details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Affero General Public License along
|
|
||||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import {useScope as useI18nScope} from '@canvas/i18n'
|
|
||||||
import {View, Collection} from '@canvas/backbone'
|
|
||||||
import _ from 'underscore'
|
|
||||||
import CollectionView from '@canvas/backbone-collection-view'
|
|
||||||
import OutcomeView from './OutcomeView.coffee'
|
|
||||||
import template from '../../jst/group.handlebars'
|
|
||||||
|
|
||||||
I18n = useI18nScope('grade_summaryGroupView')
|
|
||||||
|
|
||||||
export default class GroupView extends View
|
|
||||||
tagName: 'li'
|
|
||||||
className: 'group'
|
|
||||||
|
|
||||||
els:
|
|
||||||
'.outcomes': '$outcomes'
|
|
||||||
|
|
||||||
events:
|
|
||||||
'click .group-description': 'expand'
|
|
||||||
'keyclick .group-description': 'expand'
|
|
||||||
|
|
||||||
template: template
|
|
||||||
|
|
||||||
render: ->
|
|
||||||
super
|
|
||||||
outcomesView = new CollectionView
|
|
||||||
el: @$outcomes
|
|
||||||
collection: @model.get('outcomes')
|
|
||||||
itemView: OutcomeView
|
|
||||||
outcomesView.render()
|
|
||||||
|
|
||||||
expand: ->
|
|
||||||
@$el.toggleClass('expanded')
|
|
||||||
if @$el.hasClass("expanded")
|
|
||||||
@$el.children("div.group-description").attr("aria-expanded", "true")
|
|
||||||
else
|
|
||||||
@$el.children("div.group-description").attr("aria-expanded", "false")
|
|
||||||
|
|
||||||
$collapseToggle = $('div.outcome-toggles a.icon-collapse')
|
|
||||||
if $('li.group.expanded').length == 0
|
|
||||||
$collapseToggle.attr('disabled', 'disabled')
|
|
||||||
$collapseToggle.attr('aria-disabled', 'true')
|
|
||||||
else
|
|
||||||
$collapseToggle.removeAttr('disabled')
|
|
||||||
$collapseToggle.attr('aria-disabled', 'false')
|
|
||||||
|
|
||||||
$expandToggle = $('div.outcome-toggles a.icon-expand')
|
|
||||||
if $('li.group:not(.expanded)').length == 0
|
|
||||||
$expandToggle.attr('disabled', 'disabled')
|
|
||||||
$expandToggle.attr('aria-disabled', 'true')
|
|
||||||
else
|
|
||||||
$expandToggle.removeAttr('disabled')
|
|
||||||
$expandToggle.attr('aria-disabled', 'false')
|
|
||||||
|
|
||||||
statusTooltip: ->
|
|
||||||
switch @model.status()
|
|
||||||
when 'undefined' then I18n.t('Unstarted')
|
|
||||||
when 'remedial' then I18n.t('Well Below Mastery')
|
|
||||||
when 'near' then I18n.t('Near Mastery')
|
|
||||||
when 'mastery' then I18n.t('Meets Mastery')
|
|
||||||
when 'exceeds' then I18n.t('Exceeds Mastery')
|
|
||||||
|
|
||||||
toJSON: ->
|
|
||||||
json = super
|
|
||||||
_.extend json,
|
|
||||||
statusTooltip: @statusTooltip()
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
//
|
||||||
|
// Copyright (C) 2014 - present Instructure, Inc.
|
||||||
|
//
|
||||||
|
// This file is part of Canvas.
|
||||||
|
//
|
||||||
|
// Canvas is free software: you can redistribute it and/or modify it under
|
||||||
|
// the terms of the GNU Affero General Public License as published by the Free
|
||||||
|
// Software Foundation, version 3 of the License.
|
||||||
|
//
|
||||||
|
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||||
|
// details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License along
|
||||||
|
// with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import {useScope as useI18nScope} from '@canvas/i18n'
|
||||||
|
import $ from 'jquery'
|
||||||
|
import {View} from '@canvas/backbone'
|
||||||
|
import CollectionView from '@canvas/backbone-collection-view'
|
||||||
|
import OutcomeView from './OutcomeView'
|
||||||
|
import template from '../../jst/group.handlebars'
|
||||||
|
|
||||||
|
const I18n = useI18nScope('grade_summaryGroupView')
|
||||||
|
|
||||||
|
class GroupView extends View {
|
||||||
|
render() {
|
||||||
|
super.render(...arguments)
|
||||||
|
const outcomesView = new CollectionView({
|
||||||
|
el: this.$outcomes,
|
||||||
|
collection: this.model.get('outcomes'),
|
||||||
|
itemView: OutcomeView,
|
||||||
|
})
|
||||||
|
return outcomesView.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
expand() {
|
||||||
|
this.$el.toggleClass('expanded')
|
||||||
|
if (this.$el.hasClass('expanded')) {
|
||||||
|
this.$el.children('div.group-description').attr('aria-expanded', 'true')
|
||||||
|
} else {
|
||||||
|
this.$el.children('div.group-description').attr('aria-expanded', 'false')
|
||||||
|
}
|
||||||
|
|
||||||
|
const $collapseToggle = $('div.outcome-toggles a.icon-collapse')
|
||||||
|
if ($('li.group.expanded').length === 0) {
|
||||||
|
$collapseToggle.attr('disabled', 'disabled')
|
||||||
|
$collapseToggle.attr('aria-disabled', 'true')
|
||||||
|
} else {
|
||||||
|
$collapseToggle.removeAttr('disabled')
|
||||||
|
$collapseToggle.attr('aria-disabled', 'false')
|
||||||
|
}
|
||||||
|
|
||||||
|
const $expandToggle = $('div.outcome-toggles a.icon-expand')
|
||||||
|
if ($('li.group:not(.expanded)').length === 0) {
|
||||||
|
$expandToggle.attr('disabled', 'disabled')
|
||||||
|
return $expandToggle.attr('aria-disabled', 'true')
|
||||||
|
} else {
|
||||||
|
$expandToggle.removeAttr('disabled')
|
||||||
|
return $expandToggle.attr('aria-disabled', 'false')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
statusTooltip() {
|
||||||
|
switch (this.model.status()) {
|
||||||
|
case 'undefined':
|
||||||
|
return I18n.t('Unstarted')
|
||||||
|
case 'remedial':
|
||||||
|
return I18n.t('Well Below Mastery')
|
||||||
|
case 'near':
|
||||||
|
return I18n.t('Near Mastery')
|
||||||
|
case 'mastery':
|
||||||
|
return I18n.t('Meets Mastery')
|
||||||
|
case 'exceeds':
|
||||||
|
return I18n.t('Exceeds Mastery')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
return {
|
||||||
|
...super.toJSON(...arguments),
|
||||||
|
statusTooltip: this.statusTooltip(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupView.prototype.template = template
|
||||||
|
GroupView.prototype.tagName = 'li'
|
||||||
|
GroupView.prototype.className = 'group'
|
||||||
|
GroupView.prototype.els = {'.outcomes': '$outcomes'}
|
||||||
|
GroupView.prototype.events = {
|
||||||
|
'click .group-description': 'expand',
|
||||||
|
'keyclick .group-description': 'expand',
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GroupView
|
|
@ -16,18 +16,14 @@
|
||||||
// with this program. If not, see <http://www.gnu.org/licenses/>.
|
// with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import Backbone from '@canvas/backbone'
|
import Backbone from '@canvas/backbone'
|
||||||
import OutcomeResultCollection from '../collections/OutcomeResultCollection.coffee'
|
import OutcomeResultCollection from '../collections/OutcomeResultCollection'
|
||||||
import DialogBaseView from '@canvas/dialog-base-view'
|
import DialogBaseView from '@canvas/dialog-base-view'
|
||||||
import CollectionView from '@canvas/backbone-collection-view'
|
import CollectionView from '@canvas/backbone-collection-view'
|
||||||
import AlignmentView from './AlignmentView'
|
import AlignmentView from './AlignmentView'
|
||||||
import ProgressBarView from './ProgressBarView.coffee'
|
import ProgressBarView from './ProgressBarView'
|
||||||
import template from '../../jst/outcome_detail.handlebars'
|
import template from '../../jst/outcome_detail.handlebars'
|
||||||
|
|
||||||
export default class OutcomeDetailView extends DialogBaseView {
|
class OutcomeDetailView extends DialogBaseView {
|
||||||
static initClass() {
|
|
||||||
this.prototype.template = template
|
|
||||||
}
|
|
||||||
|
|
||||||
dialogOptions() {
|
dialogOptions() {
|
||||||
return {
|
return {
|
||||||
containerId: 'outcome_detail',
|
containerId: 'outcome_detail',
|
||||||
|
@ -77,4 +73,7 @@ export default class OutcomeDetailView extends DialogBaseView {
|
||||||
return {...json, progress: this.progress}
|
return {...json, progress: this.progress}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OutcomeDetailView.initClass()
|
|
||||||
|
OutcomeDetailView.prototype.template = template
|
||||||
|
|
||||||
|
export default OutcomeDetailView
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
#
|
|
||||||
# Copyright (C) 2015 - present Instructure, Inc.
|
|
||||||
#
|
|
||||||
# This file is part of Canvas.
|
|
||||||
#
|
|
||||||
# Canvas is free software: you can redistribute it and/or modify it under
|
|
||||||
# the terms of the GNU Affero General Public License as published by the Free
|
|
||||||
# Software Foundation, version 3 of the License.
|
|
||||||
#
|
|
||||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
||||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
||||||
# details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Affero General Public License along
|
|
||||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import $ from 'jquery'
|
|
||||||
import _ from 'underscore'
|
|
||||||
import DialogBaseView from '@canvas/dialog-base-view'
|
|
||||||
import OutcomeLineGraphView from './OutcomeLineGraphView.coffee'
|
|
||||||
import template from '@canvas/outcomes/jst/outcomePopover.handlebars'
|
|
||||||
|
|
||||||
export default class OutcomeResultsDialogView extends DialogBaseView
|
|
||||||
@optionProperty 'model'
|
|
||||||
$target: null
|
|
||||||
template: template
|
|
||||||
|
|
||||||
initialize: ->
|
|
||||||
super
|
|
||||||
@outcomeLineGraphView = new OutcomeLineGraphView({
|
|
||||||
model: @model
|
|
||||||
})
|
|
||||||
|
|
||||||
afterRender: ->
|
|
||||||
@outcomeLineGraphView.setElement(@$("div.line-graph"))
|
|
||||||
@outcomeLineGraphView.render()
|
|
||||||
|
|
||||||
dialogOptions: ->
|
|
||||||
containerId: "outcome_results_dialog"
|
|
||||||
close: @onClose
|
|
||||||
buttons: []
|
|
||||||
width: 460
|
|
||||||
|
|
||||||
show: (e) ->
|
|
||||||
return unless (e.type == "click" || @_getKey(e.keyCode))
|
|
||||||
@$target = $(e.target)
|
|
||||||
e.preventDefault()
|
|
||||||
@$el.dialog('option', 'title', @model.get('title'))
|
|
||||||
super
|
|
||||||
@render()
|
|
||||||
|
|
||||||
onClose: =>
|
|
||||||
@$target.focus()
|
|
||||||
delete @$target
|
|
||||||
|
|
||||||
toJSON: ->
|
|
||||||
json = super
|
|
||||||
_.extend json,
|
|
||||||
dialog: true
|
|
||||||
|
|
||||||
# Private
|
|
||||||
_getKey: (keycode) =>
|
|
||||||
keys = {
|
|
||||||
13 : "enter"
|
|
||||||
32 : "spacebar"
|
|
||||||
}
|
|
||||||
keys[keycode]
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 - present Instructure, Inc.
|
||||||
|
//
|
||||||
|
// This file is part of Canvas.
|
||||||
|
//
|
||||||
|
// Canvas is free software: you can redistribute it and/or modify it under
|
||||||
|
// the terms of the GNU Affero General Public License as published by the Free
|
||||||
|
// Software Foundation, version 3 of the License.
|
||||||
|
//
|
||||||
|
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||||
|
// details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License along
|
||||||
|
// with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import $ from 'jquery'
|
||||||
|
import DialogBaseView from '@canvas/dialog-base-view'
|
||||||
|
import OutcomeLineGraphView from './OutcomeLineGraphView'
|
||||||
|
import template from '@canvas/outcomes/jst/outcomePopover.handlebars'
|
||||||
|
|
||||||
|
class OutcomeResultsDialogView extends DialogBaseView {
|
||||||
|
constructor(...args) {
|
||||||
|
super(...args)
|
||||||
|
this.onClose = this.onClose.bind(this)
|
||||||
|
this._getKey = this._getKey.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
super.initialize(...arguments)
|
||||||
|
return (this.outcomeLineGraphView = new OutcomeLineGraphView({
|
||||||
|
model: this.model,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
afterRender() {
|
||||||
|
this.outcomeLineGraphView.setElement(this.$('div.line-graph'))
|
||||||
|
return this.outcomeLineGraphView.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
dialogOptions() {
|
||||||
|
return {
|
||||||
|
containerId: 'outcome_results_dialog',
|
||||||
|
close: this.onClose,
|
||||||
|
buttons: [],
|
||||||
|
width: 460,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
show(e) {
|
||||||
|
if (e.type !== 'click' && !this._getKey(e.keyCode)) return
|
||||||
|
this.$target = $(e.target)
|
||||||
|
e.preventDefault()
|
||||||
|
this.$el.dialog('option', 'title', this.model.get('title'))
|
||||||
|
super.show(...arguments)
|
||||||
|
return this.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
onClose() {
|
||||||
|
this.$target.focus()
|
||||||
|
delete this.$target
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
return {
|
||||||
|
...super.toJSON(...arguments),
|
||||||
|
dialog: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private
|
||||||
|
_getKey(keycode) {
|
||||||
|
const keys = {
|
||||||
|
13: 'enter',
|
||||||
|
32: 'spacebar',
|
||||||
|
}
|
||||||
|
return keys[keycode]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OutcomeResultsDialogView.optionProperty('model')
|
||||||
|
OutcomeResultsDialogView.prototype.$target = null
|
||||||
|
OutcomeResultsDialogView.prototype.template = template
|
||||||
|
|
||||||
|
export default OutcomeResultsDialogView
|
|
@ -1,285 +0,0 @@
|
||||||
#
|
|
||||||
# Copyright (C) 2015 - present Instructure, Inc.
|
|
||||||
#
|
|
||||||
# This file is part of Canvas.
|
|
||||||
#
|
|
||||||
# Canvas is free software: you can redistribute it and/or modify it under
|
|
||||||
# the terms of the GNU Affero General Public License as published by the Free
|
|
||||||
# Software Foundation, version 3 of the License.
|
|
||||||
#
|
|
||||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
||||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
||||||
# details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Affero General Public License along
|
|
||||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import _ from 'underscore'
|
|
||||||
import Backbone from '@canvas/backbone'
|
|
||||||
import I18n from '@canvas/i18n'
|
|
||||||
import OutcomeResultCollection from '../collections/OutcomeResultCollection.coffee'
|
|
||||||
import d3 from 'd3/d3'
|
|
||||||
import accessibleTemplate from '../../jst/accessibleLineGraph.handlebars'
|
|
||||||
import '../../sum'
|
|
||||||
|
|
||||||
# Trend class based on formulae found here:
|
|
||||||
# http://classroom.synonym.com/calculate-trendline-2709.html
|
|
||||||
class Trend
|
|
||||||
constructor: (@rawData) ->
|
|
||||||
|
|
||||||
# Returns: [[x1, y1], [x2, y2]]
|
|
||||||
data: ->
|
|
||||||
[[
|
|
||||||
@xValue(@rawData[0])
|
|
||||||
@yIntercept()
|
|
||||||
@xValue(_.last(@rawData))
|
|
||||||
(@slope() * @xValue(_.last(@rawData))) + @yIntercept()
|
|
||||||
]]
|
|
||||||
|
|
||||||
slope: ->
|
|
||||||
(@a() - @b()) / (@c() - @d())
|
|
||||||
|
|
||||||
yIntercept: ->
|
|
||||||
(@e() - @f()) / @n()
|
|
||||||
|
|
||||||
# The number of points of data.
|
|
||||||
n: ->
|
|
||||||
@rawData.length
|
|
||||||
|
|
||||||
# `n` times the sum of the products of each x & y pair.
|
|
||||||
a: ->
|
|
||||||
@n() * _.sum(@rawData, (point) => (@xValue(point) * @yValue(point)))
|
|
||||||
|
|
||||||
# The product of the sum of all x values and all y values.
|
|
||||||
b: ->
|
|
||||||
_.sum(@rawData, @xValue) * _.sum(@rawData, @yValue)
|
|
||||||
|
|
||||||
# `n` times the sum of all x values individually squared.
|
|
||||||
c: ->
|
|
||||||
@n() * _.sum(@rawData, (point) => Math.pow(@xValue(point), 2))
|
|
||||||
|
|
||||||
# The sum of all x values squared.
|
|
||||||
d: ->
|
|
||||||
Math.pow(_.sum(@rawData, @xValue), 2)
|
|
||||||
|
|
||||||
# The sum of all y values.
|
|
||||||
e: ->
|
|
||||||
_.sum(@rawData, @yValue)
|
|
||||||
|
|
||||||
# The slope times the sum of all x values.
|
|
||||||
f: ->
|
|
||||||
@slope() * _.sum(@rawData, @xValue)
|
|
||||||
|
|
||||||
xValue: (point) ->
|
|
||||||
point.x
|
|
||||||
|
|
||||||
yValue: (point) ->
|
|
||||||
point.y
|
|
||||||
|
|
||||||
export default class OutcomeLineGraphView extends Backbone.View
|
|
||||||
@optionProperty 'el'
|
|
||||||
@optionProperty 'height'
|
|
||||||
@optionProperty 'limit'
|
|
||||||
@optionProperty 'margin'
|
|
||||||
@optionProperty 'model'
|
|
||||||
@optionProperty 'timeFormat'
|
|
||||||
defaults:
|
|
||||||
height: 200
|
|
||||||
limit: 8
|
|
||||||
margin: {top: 20, right: 20, bottom: 30, left: 40}
|
|
||||||
# 2015-02-06T17:49:08Z
|
|
||||||
timeFormat: "%Y-%m-%dT%XZ"
|
|
||||||
|
|
||||||
initialize: ->
|
|
||||||
super
|
|
||||||
@deferred = $.Deferred()
|
|
||||||
@collection = new OutcomeResultCollection([], {
|
|
||||||
outcome: @model
|
|
||||||
})
|
|
||||||
@collection.on 'fetched:last', =>
|
|
||||||
@deferred.resolve()
|
|
||||||
@collection.fetch()
|
|
||||||
|
|
||||||
render: ->
|
|
||||||
if @deferred.isResolved()
|
|
||||||
return @ if @collection.isEmpty()
|
|
||||||
|
|
||||||
@_prepareScales()
|
|
||||||
@_prepareAxes()
|
|
||||||
@_prepareLines()
|
|
||||||
|
|
||||||
@svg = d3.select(@el)
|
|
||||||
.append("svg")
|
|
||||||
.attr("width", @width() + @margin.left + @margin.right)
|
|
||||||
.attr("height", @height + @margin.top + @margin.bottom)
|
|
||||||
.attr("aria-hidden", true)
|
|
||||||
.append("g")
|
|
||||||
.attr("transform", "translate(#{@margin.left}, #{@margin.top})")
|
|
||||||
|
|
||||||
@_appendAxes()
|
|
||||||
@_appendLines()
|
|
||||||
|
|
||||||
@$('.screenreader-only').append(accessibleTemplate(@toJSON()))
|
|
||||||
else
|
|
||||||
@deferred.done(@render)
|
|
||||||
|
|
||||||
|
|
||||||
@
|
|
||||||
|
|
||||||
toJSON: ->
|
|
||||||
current_user_name: ENV.current_user.display_name
|
|
||||||
data: @data()
|
|
||||||
outcome_name: @model.get('friendly_name')
|
|
||||||
|
|
||||||
# Data helpers
|
|
||||||
data: ->
|
|
||||||
@_data ?= @collection.chain()
|
|
||||||
.last(@limit)
|
|
||||||
.map((outcomeResult, i) =>
|
|
||||||
x: i
|
|
||||||
y: @percentageFor(outcomeResult.get('score'))
|
|
||||||
date: outcomeResult.get('submitted_or_assessed_at')
|
|
||||||
).value()
|
|
||||||
|
|
||||||
masteryPercentage: ->
|
|
||||||
if @model.get('points_possible') > 0
|
|
||||||
(@model.get('mastery_points') / @model.get('points_possible')) * 100
|
|
||||||
else
|
|
||||||
100
|
|
||||||
|
|
||||||
percentageFor: (score) ->
|
|
||||||
if @model.get('points_possible') > 0
|
|
||||||
((score / @model.get('points_possible')) * 100)
|
|
||||||
else
|
|
||||||
((score / @model.get('mastery_points')) * 100)
|
|
||||||
|
|
||||||
xValue: (point) =>
|
|
||||||
@x(point.x)
|
|
||||||
|
|
||||||
yValue: (point) =>
|
|
||||||
@y(point.y)
|
|
||||||
|
|
||||||
# View helpers
|
|
||||||
_appendAxes: ->
|
|
||||||
@svg.append("g")
|
|
||||||
.attr("class", "x axis")
|
|
||||||
.attr("transform", "translate(0,#{@height})")
|
|
||||||
.call(@xAxis)
|
|
||||||
|
|
||||||
@svg.append("g")
|
|
||||||
.attr("class", "date-guides")
|
|
||||||
.attr("transform", "translate(0,#{@height})")
|
|
||||||
.call(@dateGuides)
|
|
||||||
|
|
||||||
@svg.append("g")
|
|
||||||
.attr("class", "y axis")
|
|
||||||
.call(@yAxis)
|
|
||||||
|
|
||||||
@svg.append("g")
|
|
||||||
.attr("class", "guides")
|
|
||||||
.call(@yGuides)
|
|
||||||
|
|
||||||
@svg.append("g")
|
|
||||||
.attr("class", "mastery-percentage-guide")
|
|
||||||
.style("stroke-dasharray", ("3, 3"))
|
|
||||||
.call(@masteryPercentageGuide)
|
|
||||||
|
|
||||||
_appendLines: ->
|
|
||||||
@svg.selectAll("circle")
|
|
||||||
.data(@data())
|
|
||||||
.enter().append("circle")
|
|
||||||
.attr("fill", "black")
|
|
||||||
.attr("r", 3)
|
|
||||||
.attr("cx", @xValue)
|
|
||||||
.attr("cy", @yValue)
|
|
||||||
|
|
||||||
@svg.append("path")
|
|
||||||
.datum(@data())
|
|
||||||
.attr("d", @line)
|
|
||||||
.attr("class", "line")
|
|
||||||
.attr("stroke", "black")
|
|
||||||
.attr("stroke-width", 1)
|
|
||||||
.attr("fill", "none")
|
|
||||||
|
|
||||||
if @trend?
|
|
||||||
@svg.selectAll(".trendline")
|
|
||||||
.data(@trend.data())
|
|
||||||
.enter()
|
|
||||||
.append("line")
|
|
||||||
.attr("class", "trendline")
|
|
||||||
.attr("x1", (d) => @x(d[0]))
|
|
||||||
.attr("y1", (d) => @y(d[1]))
|
|
||||||
.attr("x2", (d) => @x(d[2]))
|
|
||||||
.attr("y2", (d) => @y(d[3]))
|
|
||||||
.attr("stroke-width", 1)
|
|
||||||
|
|
||||||
@svg
|
|
||||||
|
|
||||||
|
|
||||||
_prepareAxes: ->
|
|
||||||
@xAxis = d3.svg.axis()
|
|
||||||
.scale(@x)
|
|
||||||
.tickFormat('')
|
|
||||||
@dateGuides = d3.svg.axis()
|
|
||||||
.scale(@xTimeScale)
|
|
||||||
.tickValues([
|
|
||||||
_.first(@data()).date
|
|
||||||
_.last(@data()).date
|
|
||||||
])
|
|
||||||
.tickFormat((d) -> Intl.DateTimeFormat(I18n.currentLocale(), { day: 'numeric', month: 'numeric'}).format(d))
|
|
||||||
@yAxis = d3.svg.axis()
|
|
||||||
.scale(@y)
|
|
||||||
.orient("left")
|
|
||||||
.tickFormat((d) -> I18n.n(d, { percentage: true }))
|
|
||||||
.tickValues([0, 50, 100])
|
|
||||||
@yGuides = d3.svg.axis()
|
|
||||||
.scale(@y)
|
|
||||||
.orient("left")
|
|
||||||
.tickValues([50, 100])
|
|
||||||
.tickSize(-@width(), 0, 0)
|
|
||||||
.tickFormat("")
|
|
||||||
@masteryPercentageGuide = d3.svg.axis()
|
|
||||||
.scale(@y)
|
|
||||||
.orient("left")
|
|
||||||
.tickValues([@masteryPercentage()])
|
|
||||||
.tickSize(-@width(), 0, 0)
|
|
||||||
.tickFormat("")
|
|
||||||
|
|
||||||
_prepareLines: ->
|
|
||||||
if @data().length >=3
|
|
||||||
@trend = new Trend(@data())
|
|
||||||
|
|
||||||
@line = d3.svg.line()
|
|
||||||
.x(@xValue)
|
|
||||||
.y(@yValue)
|
|
||||||
.interpolate('linear')
|
|
||||||
|
|
||||||
_prepareScales: ->
|
|
||||||
@x = d3.scale.linear()
|
|
||||||
.range([0, @width()])
|
|
||||||
.domain([0, @limit - 1])
|
|
||||||
@xTimeScale = d3.time.scale()
|
|
||||||
.range([0, @xTimeScaleWidth()])
|
|
||||||
.domain([
|
|
||||||
_.first(@data()).date
|
|
||||||
_.last(@data()).date
|
|
||||||
])
|
|
||||||
@y = d3.scale.linear()
|
|
||||||
.range([@height, @margin.bottom])
|
|
||||||
.domain([0, 100])
|
|
||||||
|
|
||||||
width: ->
|
|
||||||
@$el.width() - @margin.left - @margin.right - 10
|
|
||||||
|
|
||||||
# The width of the axis used to display the first and last date of scores
|
|
||||||
# displayed has to be different than the full width, in case the number
|
|
||||||
# of points is fewer than the limit (8). What we want is the width of the
|
|
||||||
# element reduced by the difference between the limit and the number of
|
|
||||||
# points we actually have, multiplied by the width each point represents,
|
|
||||||
# based on the element's width and the limit.
|
|
||||||
xTimeScaleWidth: ->
|
|
||||||
(@width() - (
|
|
||||||
(@width() / (@limit - 1)) *
|
|
||||||
(@limit - @data().length)
|
|
||||||
))
|
|
|
@ -0,0 +1,340 @@
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 - present Instructure, Inc.
|
||||||
|
//
|
||||||
|
// This file is part of Canvas.
|
||||||
|
//
|
||||||
|
// Canvas is free software: you can redistribute it and/or modify it under
|
||||||
|
// the terms of the GNU Affero General Public License as published by the Free
|
||||||
|
// Software Foundation, version 3 of the License.
|
||||||
|
//
|
||||||
|
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||||
|
// details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License along
|
||||||
|
// with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* decaffeinate suggestions:
|
||||||
|
* DS207: Consider shorter variations of null checks
|
||||||
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
|
||||||
|
*/
|
||||||
|
|
||||||
|
import $ from 'jquery'
|
||||||
|
import {sumBy} from 'lodash'
|
||||||
|
import Backbone from '@canvas/backbone'
|
||||||
|
import I18n from '@canvas/i18n'
|
||||||
|
import OutcomeResultCollection from '../collections/OutcomeResultCollection'
|
||||||
|
import d3 from 'd3/d3'
|
||||||
|
import accessibleTemplate from '../../jst/accessibleLineGraph.handlebars'
|
||||||
|
|
||||||
|
const first = array => array.at(0)
|
||||||
|
const last = array => array.at(-1)
|
||||||
|
|
||||||
|
// Trend class based on formulae found here:
|
||||||
|
// http://classroom.synonym.com/calculate-trendline-2709.html
|
||||||
|
class Trend {
|
||||||
|
constructor(rawData) {
|
||||||
|
this.rawData = rawData
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns: [[x1, y1], [x2, y2]]
|
||||||
|
data() {
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
this.rawData[0].x,
|
||||||
|
this.yIntercept(),
|
||||||
|
last(this.rawData).x,
|
||||||
|
this.slope() * last(this.rawData).x + this.yIntercept(),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
slope() {
|
||||||
|
return (this.a() - this.b()) / (this.c() - this.d())
|
||||||
|
}
|
||||||
|
|
||||||
|
yIntercept() {
|
||||||
|
return (this.e() - this.f()) / this.n()
|
||||||
|
}
|
||||||
|
|
||||||
|
// The number of points of data.
|
||||||
|
n() {
|
||||||
|
return this.rawData.length
|
||||||
|
}
|
||||||
|
|
||||||
|
// `n` times the sum of the products of each x & y pair.
|
||||||
|
a() {
|
||||||
|
return this.n() * sumBy(this.rawData, p => p.x * p.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The product of the sum of all x values and all y values.
|
||||||
|
b() {
|
||||||
|
return sumBy(this.rawData, p => p.x) * sumBy(this.rawData, p => p.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
// `n` times the sum of all x values individually squared.
|
||||||
|
c() {
|
||||||
|
return this.n() * sumBy(this.rawData, p => p.x * p.x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The sum of all x values squared.
|
||||||
|
d() {
|
||||||
|
return Math.pow(
|
||||||
|
sumBy(this.rawData, p => p.x),
|
||||||
|
2
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The sum of all y values.
|
||||||
|
e() {
|
||||||
|
return sumBy(this.rawData, p => p.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The slope times the sum of all x values.
|
||||||
|
f() {
|
||||||
|
return this.slope() * sumBy(this.rawData, p => p.x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OutcomeLineGraphView extends Backbone.View {
|
||||||
|
constructor(...args) {
|
||||||
|
super(...args)
|
||||||
|
this.xValue = this.xValue.bind(this)
|
||||||
|
this.yValue = this.yValue.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
super.initialize(...arguments)
|
||||||
|
this.deferred = $.Deferred()
|
||||||
|
this.collection = new OutcomeResultCollection([], {
|
||||||
|
outcome: this.model,
|
||||||
|
})
|
||||||
|
this.collection.on('fetched:last', () => {
|
||||||
|
return this.deferred.resolve()
|
||||||
|
})
|
||||||
|
return this.collection.fetch()
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.deferred.isResolved()) {
|
||||||
|
if (this.collection.isEmpty()) {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
this._prepareScales()
|
||||||
|
this._prepareAxes()
|
||||||
|
this._prepareLines()
|
||||||
|
|
||||||
|
this.svg = d3
|
||||||
|
.select(this.el)
|
||||||
|
.append('svg')
|
||||||
|
.attr('width', this.width() + this.margin.left + this.margin.right)
|
||||||
|
.attr('height', this.height + this.margin.top + this.margin.bottom)
|
||||||
|
.attr('aria-hidden', true)
|
||||||
|
.append('g')
|
||||||
|
.attr('transform', `translate(${this.margin.left}, ${this.margin.top})`)
|
||||||
|
|
||||||
|
this._appendAxes()
|
||||||
|
this._appendLines()
|
||||||
|
|
||||||
|
this.$('.screenreader-only').append(accessibleTemplate(this.toJSON()))
|
||||||
|
} else {
|
||||||
|
this.deferred.done(this.render)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
return {
|
||||||
|
current_user_name: ENV.current_user.display_name,
|
||||||
|
data: this.data(),
|
||||||
|
outcome_name: this.model.get('friendly_name'),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data helpers
|
||||||
|
data() {
|
||||||
|
if (this._data === null || typeof this._data === 'undefined') {
|
||||||
|
this._data = this.collection
|
||||||
|
.chain()
|
||||||
|
.last(this.limit)
|
||||||
|
.map((outcomeResult, i) => ({
|
||||||
|
x: i,
|
||||||
|
y: this.percentageFor(outcomeResult.get('score')),
|
||||||
|
date: outcomeResult.get('submitted_or_assessed_at'),
|
||||||
|
}))
|
||||||
|
.value()
|
||||||
|
}
|
||||||
|
return this._data
|
||||||
|
}
|
||||||
|
|
||||||
|
masteryPercentage() {
|
||||||
|
if (this.model.get('points_possible') > 0) {
|
||||||
|
return (this.model.get('mastery_points') / this.model.get('points_possible')) * 100
|
||||||
|
} else {
|
||||||
|
return 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
percentageFor(score) {
|
||||||
|
if (this.model.get('points_possible') > 0) {
|
||||||
|
return (score / this.model.get('points_possible')) * 100
|
||||||
|
} else {
|
||||||
|
return (score / this.model.get('mastery_points')) * 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xValue(point) {
|
||||||
|
return this.x(point.x)
|
||||||
|
}
|
||||||
|
|
||||||
|
yValue(point) {
|
||||||
|
return this.y(point.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
// View helpers
|
||||||
|
_appendAxes() {
|
||||||
|
this.svg
|
||||||
|
.append('g')
|
||||||
|
.attr('class', 'x axis')
|
||||||
|
.attr('transform', `translate(0,${this.height})`)
|
||||||
|
.call(this.xAxis)
|
||||||
|
|
||||||
|
this.svg
|
||||||
|
.append('g')
|
||||||
|
.attr('class', 'date-guides')
|
||||||
|
.attr('transform', `translate(0,${this.height})`)
|
||||||
|
.call(this.dateGuides)
|
||||||
|
|
||||||
|
this.svg.append('g').attr('class', 'y axis').call(this.yAxis)
|
||||||
|
|
||||||
|
this.svg.append('g').attr('class', 'guides').call(this.yGuides)
|
||||||
|
|
||||||
|
return this.svg
|
||||||
|
.append('g')
|
||||||
|
.attr('class', 'mastery-percentage-guide')
|
||||||
|
.style('stroke-dasharray', '3, 3')
|
||||||
|
.call(this.masteryPercentageGuide)
|
||||||
|
}
|
||||||
|
|
||||||
|
_appendLines() {
|
||||||
|
this.svg
|
||||||
|
.selectAll('circle')
|
||||||
|
.data(this.data())
|
||||||
|
.enter()
|
||||||
|
.append('circle')
|
||||||
|
.attr('fill', 'black')
|
||||||
|
.attr('r', 3)
|
||||||
|
.attr('cx', this.xValue)
|
||||||
|
.attr('cy', this.yValue)
|
||||||
|
|
||||||
|
this.svg
|
||||||
|
.append('path')
|
||||||
|
.datum(this.data())
|
||||||
|
.attr('d', this.line)
|
||||||
|
.attr('class', 'line')
|
||||||
|
.attr('stroke', 'black')
|
||||||
|
.attr('stroke-width', 1)
|
||||||
|
.attr('fill', 'none')
|
||||||
|
|
||||||
|
if (this.trend != null) {
|
||||||
|
this.svg
|
||||||
|
.selectAll('.trendline')
|
||||||
|
.data(this.trend.data())
|
||||||
|
.enter()
|
||||||
|
.append('line')
|
||||||
|
.attr('class', 'trendline')
|
||||||
|
.attr('x1', d => this.x(d[0]))
|
||||||
|
.attr('y1', d => this.y(d[1]))
|
||||||
|
.attr('x2', d => this.x(d[2]))
|
||||||
|
.attr('y2', d => this.y(d[3]))
|
||||||
|
.attr('stroke-width', 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.svg
|
||||||
|
}
|
||||||
|
|
||||||
|
_prepareAxes() {
|
||||||
|
this.xAxis = d3.svg.axis().scale(this.x).tickFormat('')
|
||||||
|
this.dateGuides = d3.svg
|
||||||
|
.axis()
|
||||||
|
.scale(this.xTimeScale)
|
||||||
|
.tickValues([first(this.data()).date, last(this.data()).date])
|
||||||
|
.tickFormat(d =>
|
||||||
|
Intl.DateTimeFormat(I18n.currentLocale(), {day: 'numeric', month: 'numeric'}).format(d)
|
||||||
|
)
|
||||||
|
this.yAxis = d3.svg
|
||||||
|
.axis()
|
||||||
|
.scale(this.y)
|
||||||
|
.orient('left')
|
||||||
|
.tickFormat(d => I18n.n(d, {percentage: true}))
|
||||||
|
.tickValues([0, 50, 100])
|
||||||
|
this.yGuides = d3.svg
|
||||||
|
.axis()
|
||||||
|
.scale(this.y)
|
||||||
|
.orient('left')
|
||||||
|
.tickValues([50, 100])
|
||||||
|
.tickSize(-this.width(), 0, 0)
|
||||||
|
.tickFormat('')
|
||||||
|
return (this.masteryPercentageGuide = d3.svg
|
||||||
|
.axis()
|
||||||
|
.scale(this.y)
|
||||||
|
.orient('left')
|
||||||
|
.tickValues([this.masteryPercentage()])
|
||||||
|
.tickSize(-this.width(), 0, 0)
|
||||||
|
.tickFormat(''))
|
||||||
|
}
|
||||||
|
|
||||||
|
_prepareLines() {
|
||||||
|
if (this.data().length >= 3) {
|
||||||
|
this.trend = new Trend(this.data())
|
||||||
|
}
|
||||||
|
|
||||||
|
return (this.line = d3.svg.line().x(this.xValue).y(this.yValue).interpolate('linear'))
|
||||||
|
}
|
||||||
|
|
||||||
|
_prepareScales() {
|
||||||
|
this.x = d3.scale
|
||||||
|
.linear()
|
||||||
|
.range([0, this.width()])
|
||||||
|
.domain([0, this.limit - 1])
|
||||||
|
this.xTimeScale = d3.time
|
||||||
|
.scale()
|
||||||
|
.range([0, this.xTimeScaleWidth()])
|
||||||
|
.domain([first(this.data()).date, last(this.data()).date])
|
||||||
|
return (this.y = d3.scale.linear().range([this.height, this.margin.bottom]).domain([0, 100]))
|
||||||
|
}
|
||||||
|
|
||||||
|
width() {
|
||||||
|
return this.$el.width() - this.margin.left - this.margin.right - 10
|
||||||
|
}
|
||||||
|
|
||||||
|
// The width of the axis used to display the first and last date of scores
|
||||||
|
// displayed has to be different than the full width, in case the number
|
||||||
|
// of points is fewer than the limit (8). What we want is the width of the
|
||||||
|
// element reduced by the difference between the limit and the number of
|
||||||
|
// points we actually have, multiplied by the width each point represents,
|
||||||
|
// based on the element's width and the limit.
|
||||||
|
xTimeScaleWidth() {
|
||||||
|
return this.width() - (this.width() / (this.limit - 1)) * (this.limit - this.data().length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OutcomeLineGraphView.optionProperty('el')
|
||||||
|
OutcomeLineGraphView.optionProperty('height')
|
||||||
|
OutcomeLineGraphView.optionProperty('limit')
|
||||||
|
OutcomeLineGraphView.optionProperty('margin')
|
||||||
|
OutcomeLineGraphView.optionProperty('model')
|
||||||
|
OutcomeLineGraphView.optionProperty('timeFormat')
|
||||||
|
OutcomeLineGraphView.prototype.defaults = {
|
||||||
|
height: 200,
|
||||||
|
limit: 8,
|
||||||
|
margin: {top: 20, right: 20, bottom: 30, left: 40},
|
||||||
|
// 2015-02-06T17:49:08Z
|
||||||
|
timeFormat: '%Y-%m-%dT%XZ',
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OutcomeLineGraphView
|
|
@ -1,71 +0,0 @@
|
||||||
#
|
|
||||||
# Copyright (C) 2015 - present Instructure, Inc.
|
|
||||||
#
|
|
||||||
# This file is part of Canvas.
|
|
||||||
#
|
|
||||||
# Canvas is free software: you can redistribute it and/or modify it under
|
|
||||||
# the terms of the GNU Affero General Public License as published by the Free
|
|
||||||
# Software Foundation, version 3 of the License.
|
|
||||||
#
|
|
||||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
||||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
||||||
# details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Affero General Public License along
|
|
||||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import Backbone from '@canvas/backbone'
|
|
||||||
import Popover from 'jquery-popover'
|
|
||||||
import OutcomeLineGraphView from './OutcomeLineGraphView.coffee'
|
|
||||||
import template from '@canvas/outcomes/jst/outcomePopover.handlebars'
|
|
||||||
|
|
||||||
export default class OutcomePopoverView extends Backbone.View
|
|
||||||
TIMEOUT_LENGTH: 50
|
|
||||||
|
|
||||||
@optionProperty 'el'
|
|
||||||
@optionProperty 'model'
|
|
||||||
|
|
||||||
events:
|
|
||||||
'click i': 'mouseleave'
|
|
||||||
'mouseenter i': 'mouseenter'
|
|
||||||
'mouseleave i': 'mouseleave'
|
|
||||||
inside: false
|
|
||||||
|
|
||||||
initialize: ->
|
|
||||||
super
|
|
||||||
@outcomeLineGraphView = new OutcomeLineGraphView({
|
|
||||||
model: @model
|
|
||||||
})
|
|
||||||
|
|
||||||
# Overrides
|
|
||||||
render: ->
|
|
||||||
template(@toJSON())
|
|
||||||
|
|
||||||
# Instance methods
|
|
||||||
closePopover: (e) ->
|
|
||||||
e?.preventDefault()
|
|
||||||
return true unless @popover?
|
|
||||||
@popover.hide()
|
|
||||||
delete @popover
|
|
||||||
|
|
||||||
mouseenter: (e) =>
|
|
||||||
@openPopover(e)
|
|
||||||
@inside = true
|
|
||||||
|
|
||||||
mouseleave: (e) =>
|
|
||||||
@inside = false
|
|
||||||
setTimeout =>
|
|
||||||
return if @inside || !@popover
|
|
||||||
@closePopover()
|
|
||||||
, @TIMEOUT_LENGTH
|
|
||||||
|
|
||||||
openPopover: (e) ->
|
|
||||||
if @closePopover()
|
|
||||||
@popover = new Popover(e, @render(), {
|
|
||||||
verticalSide: 'bottom'
|
|
||||||
manualOffset: 14
|
|
||||||
})
|
|
||||||
@outcomeLineGraphView.setElement(@popover.el.find("div.line-graph"))
|
|
||||||
@outcomeLineGraphView.render()
|
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015 - present Instructure, Inc.
|
||||||
|
//
|
||||||
|
// This file is part of Canvas.
|
||||||
|
//
|
||||||
|
// Canvas is free software: you can redistribute it and/or modify it under
|
||||||
|
// the terms of the GNU Affero General Public License as published by the Free
|
||||||
|
// Software Foundation, version 3 of the License.
|
||||||
|
//
|
||||||
|
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||||
|
// details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License along
|
||||||
|
// with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/*
|
||||||
|
* decaffeinate suggestions:
|
||||||
|
* DS206: Consider reworking classes to avoid initClass
|
||||||
|
* DS207: Consider shorter variations of null checks
|
||||||
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Backbone from '@canvas/backbone'
|
||||||
|
import Popover from 'jquery-popover'
|
||||||
|
import OutcomeLineGraphView from './OutcomeLineGraphView'
|
||||||
|
import template from '@canvas/outcomes/jst/outcomePopover.handlebars'
|
||||||
|
|
||||||
|
const TIMEOUT_LENGTH = 50
|
||||||
|
|
||||||
|
class OutcomePopoverView extends Backbone.View {
|
||||||
|
constructor(...args) {
|
||||||
|
super(...args)
|
||||||
|
this.mouseenter = this.mouseenter.bind(this)
|
||||||
|
this.mouseleave = this.mouseleave.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
super.initialize(...arguments)
|
||||||
|
return (this.outcomeLineGraphView = new OutcomeLineGraphView({
|
||||||
|
model: this.model,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overrides
|
||||||
|
render() {
|
||||||
|
return template(this.toJSON())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instance methods
|
||||||
|
closePopover(e) {
|
||||||
|
e?.preventDefault()
|
||||||
|
if (this.popover === null || typeof this.popover === 'undefined') return true
|
||||||
|
this.popover.hide()
|
||||||
|
return delete this.popover
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseenter(e) {
|
||||||
|
this.openPopover(e)
|
||||||
|
this.inside = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseleave(_e) {
|
||||||
|
this.inside = false
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!this.inside && this.popover) this.closePopover()
|
||||||
|
}, TIMEOUT_LENGTH)
|
||||||
|
}
|
||||||
|
|
||||||
|
openPopover(e) {
|
||||||
|
if (this.closePopover()) {
|
||||||
|
this.popover = new Popover(e, this.render(), {
|
||||||
|
verticalSide: 'bottom',
|
||||||
|
manualOffset: 14,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.outcomeLineGraphView.setElement(this.popover.el.find('div.line-graph'))
|
||||||
|
return this.outcomeLineGraphView.render()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OutcomePopoverView.prototype.events = {
|
||||||
|
'click i': 'mouseleave',
|
||||||
|
'mouseenter i': 'mouseenter',
|
||||||
|
'mouseleave i': 'mouseleave',
|
||||||
|
}
|
||||||
|
OutcomePopoverView.prototype.inside = false
|
||||||
|
OutcomePopoverView.prototype.TIMEOUT_LENGTH = TIMEOUT_LENGTH
|
||||||
|
|
||||||
|
OutcomePopoverView.optionProperty('el')
|
||||||
|
OutcomePopoverView.optionProperty('model')
|
||||||
|
|
||||||
|
export default OutcomePopoverView
|
|
@ -1,53 +0,0 @@
|
||||||
#
|
|
||||||
# Copyright (C) 2014 - present Instructure, Inc.
|
|
||||||
#
|
|
||||||
# This file is part of Canvas.
|
|
||||||
#
|
|
||||||
# Canvas is free software: you can redistribute it and/or modify it under
|
|
||||||
# the terms of the GNU Affero General Public License as published by the Free
|
|
||||||
# Software Foundation, version 3 of the License.
|
|
||||||
#
|
|
||||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
||||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
||||||
# details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Affero General Public License along
|
|
||||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import _ from 'underscore'
|
|
||||||
import Backbone from '@canvas/backbone'
|
|
||||||
import ProgressBarView from './ProgressBarView.coffee'
|
|
||||||
import OutcomePopoverView from './OutcomePopoverView.coffee'
|
|
||||||
import OutcomeDialogView from './OutcomeDialogView.coffee'
|
|
||||||
import template from '../../jst/outcome.handlebars'
|
|
||||||
|
|
||||||
export default class OutcomeView extends Backbone.View
|
|
||||||
className: 'outcome'
|
|
||||||
events:
|
|
||||||
'click .more-details' : 'show'
|
|
||||||
'keydown .more-details' : 'show'
|
|
||||||
tagName: 'li'
|
|
||||||
template: template
|
|
||||||
|
|
||||||
initialize: ->
|
|
||||||
super
|
|
||||||
@progress = new ProgressBarView(model: @model)
|
|
||||||
|
|
||||||
afterRender: ->
|
|
||||||
@popover = new OutcomePopoverView({
|
|
||||||
el: @$('.more-details')
|
|
||||||
model: @model
|
|
||||||
})
|
|
||||||
@dialog = new OutcomeDialogView({
|
|
||||||
model: @model
|
|
||||||
})
|
|
||||||
|
|
||||||
show: (e) ->
|
|
||||||
@dialog.show e
|
|
||||||
|
|
||||||
toJSON: ->
|
|
||||||
json = super
|
|
||||||
_.extend json,
|
|
||||||
progress: @progress
|
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
//
|
||||||
|
// Copyright (C) 2014 - present Instructure, Inc.
|
||||||
|
//
|
||||||
|
// This file is part of Canvas.
|
||||||
|
//
|
||||||
|
// Canvas is free software: you can redistribute it and/or modify it under
|
||||||
|
// the terms of the GNU Affero General Public License as published by the Free
|
||||||
|
// Software Foundation, version 3 of the License.
|
||||||
|
//
|
||||||
|
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||||
|
// details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License along
|
||||||
|
// with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import Backbone from '@canvas/backbone'
|
||||||
|
import ProgressBarView from './ProgressBarView'
|
||||||
|
import OutcomePopoverView from './OutcomePopoverView'
|
||||||
|
import OutcomeDialogView from './OutcomeDialogView'
|
||||||
|
import template from '../../jst/outcome.handlebars'
|
||||||
|
|
||||||
|
class OutcomeView extends Backbone.View {
|
||||||
|
initialize() {
|
||||||
|
super.initialize(...arguments)
|
||||||
|
return (this.progress = new ProgressBarView({model: this.model}))
|
||||||
|
}
|
||||||
|
|
||||||
|
afterRender() {
|
||||||
|
this.popover = new OutcomePopoverView({
|
||||||
|
el: this.$('.more-details'),
|
||||||
|
model: this.model,
|
||||||
|
})
|
||||||
|
return (this.dialog = new OutcomeDialogView({
|
||||||
|
model: this.model,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
show(e) {
|
||||||
|
return this.dialog.show(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON() {
|
||||||
|
return {
|
||||||
|
...super.toJSON(...arguments),
|
||||||
|
progress: this.progress,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OutcomeView.prototype.className = 'outcome'
|
||||||
|
OutcomeView.prototype.tagName = 'li'
|
||||||
|
OutcomeView.prototype.template = template
|
||||||
|
OutcomeView.prototype.events = {
|
||||||
|
'click .more-details': 'show',
|
||||||
|
'keydown .more-details': 'show',
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OutcomeView
|
|
@ -1,23 +0,0 @@
|
||||||
#
|
|
||||||
# Copyright (C) 2014 - present Instructure, Inc.
|
|
||||||
#
|
|
||||||
# This file is part of Canvas.
|
|
||||||
#
|
|
||||||
# Canvas is free software: you can redistribute it and/or modify it under
|
|
||||||
# the terms of the GNU Affero General Public License as published by the Free
|
|
||||||
# Software Foundation, version 3 of the License.
|
|
||||||
#
|
|
||||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
||||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
||||||
# details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Affero General Public License along
|
|
||||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import Backbone from '@canvas/backbone'
|
|
||||||
import template from '../../jst/progress_bar.handlebars'
|
|
||||||
|
|
||||||
export default class ProgressBarView extends Backbone.View
|
|
||||||
className: 'bar'
|
|
||||||
template: template
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
//
|
||||||
|
// Copyright (C) 2014 - present Instructure, Inc.
|
||||||
|
//
|
||||||
|
// This file is part of Canvas.
|
||||||
|
//
|
||||||
|
// Canvas is free software: you can redistribute it and/or modify it under
|
||||||
|
// the terms of the GNU Affero General Public License as published by the Free
|
||||||
|
// Software Foundation, version 3 of the License.
|
||||||
|
//
|
||||||
|
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||||
|
// details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License along
|
||||||
|
// with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import Backbone from '@canvas/backbone'
|
||||||
|
import template from '../../jst/progress_bar.handlebars'
|
||||||
|
|
||||||
|
class ProgressBarView extends Backbone.View {}
|
||||||
|
|
||||||
|
ProgressBarView.prototype.className = 'bar'
|
||||||
|
ProgressBarView.prototype.template = template
|
||||||
|
|
||||||
|
export default ProgressBarView
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
import {View} from '@canvas/backbone'
|
import {View} from '@canvas/backbone'
|
||||||
import CollectionView from '@canvas/backbone-collection-view'
|
import CollectionView from '@canvas/backbone-collection-view'
|
||||||
import GroupView from './GroupView.coffee'
|
import GroupView from './GroupView'
|
||||||
import template from '../../jst/section.handlebars'
|
import template from '../../jst/section.handlebars'
|
||||||
|
|
||||||
export default class SectionView extends View {
|
export default class SectionView extends View {
|
||||||
|
|
Loading…
Reference in New Issue