purge performance gradebook
This removes the ill-fated React Gradebook.
This commit is effectively a revert of
b9534edd64
closes CNVS-32394
test plan:
* ensure 'Gradebook Performance' is no longer available
* ensure other Gradebooks are still functional
* Default Gradebook basic happy path
* Individual Gradebook (SRGB) basic happy path
Change-Id: Ie71ab4dfb17f494c2a7c17a27cd551a84e7efb96
Reviewed-on: https://gerrit.instructure.com/94005
Tested-by: Jenkins
Reviewed-by: Spencer Olson <solson@instructure.com>
QA-Review: KC Naegle <knaegle@instructure.com>
Product-Review: Christi Wruck
This commit is contained in:
parent
2c1ae23b89
commit
d0aa529dc6
|
@ -2,11 +2,10 @@ require [
|
|||
'jquery'
|
||||
'Backbone'
|
||||
'compiled/userSettings'
|
||||
'compiled/gradebook2/ReactGradebook'
|
||||
'compiled/gradebook2/Gradebook'
|
||||
'compiled/views/gradebook/NavigationPillView'
|
||||
'compiled/views/gradebook/OutcomeGradebookView'
|
||||
], ($, Backbone, userSettings, ReactGradebook, Gradebook, NavigationPillView, OutcomeGradebookView) ->
|
||||
], ($, Backbone, userSettings, Gradebook, NavigationPillView, OutcomeGradebookView) ->
|
||||
|
||||
class GradebookRouter extends Backbone.Router
|
||||
routes:
|
||||
|
@ -16,18 +15,11 @@ require [
|
|||
initialize: ->
|
||||
@isLoaded = false
|
||||
@views = {}
|
||||
@gradebook_performance_enabled = ENV.GRADEBOOK_OPTIONS.gradebook_performance_enabled
|
||||
@views.assignment = @gradebook()
|
||||
@views.assignment = new Gradebook(ENV.GRADEBOOK_OPTIONS)
|
||||
|
||||
if ENV.GRADEBOOK_OPTIONS.outcome_gradebook_enabled
|
||||
@views.outcome = @initOutcomes()
|
||||
|
||||
gradebook: ->
|
||||
if @gradebook_performance_enabled
|
||||
new ReactGradebook(ENV.GRADEBOOK_OPTIONS)
|
||||
else
|
||||
new Gradebook(ENV.GRADEBOOK_OPTIONS)
|
||||
|
||||
initOutcomes: ->
|
||||
book = new OutcomeGradebookView(
|
||||
el: $('.outcome-gradebook-container'),
|
||||
|
@ -47,7 +39,7 @@ require [
|
|||
@navigation.setActiveView(viewName) if @navigation
|
||||
$('.assignment-gradebook-container, .outcome-gradebook-container').addClass('hidden')
|
||||
$(".#{viewName}-gradebook-container").removeClass('hidden')
|
||||
@views[viewName].onShow() unless @gradebook_performance_enabled
|
||||
@views[viewName].onShow()
|
||||
userSettings.contextSet 'gradebook_tab', viewName
|
||||
|
||||
@router = new GradebookRouter
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
require ['jsx/gradebook/grid/app']
|
|
@ -2,7 +2,7 @@ define [
|
|||
'ember'
|
||||
'underscore'
|
||||
'compiled/gradebook2/GradebookHelpers'
|
||||
'jsx/gradebook/grid/constants'
|
||||
'jsx/gradebook/shared/constants'
|
||||
], (Ember, _, GradebookHelpers, GradebookConstants) ->
|
||||
|
||||
CustomColumnCellComponent = Ember.Component.extend
|
||||
|
|
|
@ -3,7 +3,7 @@ define [
|
|||
'../start_app'
|
||||
'../shared_ajax_fixtures'
|
||||
'compiled/gradebook2/GradebookHelpers'
|
||||
'jsx/gradebook/grid/constants'
|
||||
'jsx/gradebook/shared/constants'
|
||||
], (Ember, startApp, fixtures, GradebookHelpers, GradebookConstants) ->
|
||||
|
||||
{run} = Ember
|
||||
|
|
|
@ -36,7 +36,7 @@ define [
|
|||
'compiled/views/gradebook/SectionMenuView'
|
||||
'compiled/views/gradebook/GradingPeriodMenuView'
|
||||
'compiled/gradebook2/GradebookKeyboardNav'
|
||||
'jsx/gradebook/grid/helpers/columnArranger'
|
||||
'jsx/gradebook/shared/helpers/assignmentHelper'
|
||||
'compiled/api/gradingPeriodsApi'
|
||||
'jst/_avatar' #needed by row_student_name
|
||||
'jquery.ajaxJSON'
|
||||
|
@ -53,14 +53,14 @@ define [
|
|||
'jqueryui/sortable'
|
||||
'compiled/jquery.kylemenu'
|
||||
'compiled/jquery/fixDialogButtons'
|
||||
], ($, _, Backbone, tz, DataLoader, React, ReactDOM, LongTextEditor,
|
||||
KeyboardNavDialog, KeyboardNavTemplate, Slick, TotalColumnHeaderView, round,
|
||||
InputFilterView, I18n, GRADEBOOK_TRANSLATIONS, GradeCalculator, UserSettings,
|
||||
Spinner, SubmissionDetailsDialog, AssignmentGroupWeightsDialog,
|
||||
GradeDisplayWarningDialog, PostGradesFrameDialog, SubmissionCell,
|
||||
GradebookHeaderMenu, NumberCompare, htmlEscape, PostGradesStore, PostGradesApp,
|
||||
SubmissionStateMap, ColumnHeaderTemplate, GroupTotalCellTemplate, RowStudentNameTemplate,
|
||||
SectionMenuView, GradingPeriodMenuView, GradebookKeyboardNav, ColumnArranger, GradingPeriodsAPI) ->
|
||||
], (
|
||||
$, _, Backbone, tz, DataLoader, React, ReactDOM, LongTextEditor, KeyboardNavDialog, KeyboardNavTemplate, Slick,
|
||||
TotalColumnHeaderView, round, InputFilterView, I18n, GRADEBOOK_TRANSLATIONS, GradeCalculator, UserSettings,
|
||||
Spinner, SubmissionDetailsDialog, AssignmentGroupWeightsDialog, GradeDisplayWarningDialog, PostGradesFrameDialog,
|
||||
SubmissionCell, GradebookHeaderMenu, NumberCompare, htmlEscape, PostGradesStore, PostGradesApp,
|
||||
SubmissionStateMap, ColumnHeaderTemplate, GroupTotalCellTemplate, RowStudentNameTemplate, SectionMenuView,
|
||||
GradingPeriodMenuView, GradebookKeyboardNav, assignmentHelper, GradingPeriodsAPI
|
||||
) ->
|
||||
|
||||
class Gradebook
|
||||
columnWidths =
|
||||
|
@ -440,7 +440,7 @@ SectionMenuView, GradingPeriodMenuView, GradebookKeyboardNav, ColumnArranger, Gr
|
|||
compareAssignmentDueDates: (a, b) ->
|
||||
firstAssignment = a.object
|
||||
secondAssignment = b.object
|
||||
ColumnArranger.compareByDueDate(firstAssignment, secondAssignment)
|
||||
assignmentHelper.compareByDueDate(firstAssignment, secondAssignment)
|
||||
|
||||
makeCompareAssignmentCustomOrderFn: (sortOrder) =>
|
||||
sortMap = {}
|
||||
|
|
|
@ -10,7 +10,7 @@ define [
|
|||
'jst/re_upload_submissions_form'
|
||||
'underscore'
|
||||
'compiled/behaviors/authenticity_token'
|
||||
'jsx/gradebook/grid/helpers/messageStudentsWhoHelper'
|
||||
'jsx/gradebook/shared/helpers/messageStudentsWhoHelper'
|
||||
'jquery.instructure_forms'
|
||||
'jqueryui/dialog'
|
||||
'jquery.instructure_misc_helpers'
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
define [
|
||||
'jquery'
|
||||
'i18n!gradebook2'
|
||||
'jsx/gradebook/grid/constants'
|
||||
'jsx/gradebook/shared/constants'
|
||||
], ($, I18n, GradebookConstants) ->
|
||||
FLASH_ERROR_CLASS: '.ic-flash-error'
|
||||
|
||||
|
|
|
@ -1,272 +0,0 @@
|
|||
define [
|
||||
'react'
|
||||
'react-dom'
|
||||
'compiled/util/round'
|
||||
'compiled/views/InputFilterView'
|
||||
'i18n!gradebook2'
|
||||
'compiled/gradebook2/GradebookTranslations'
|
||||
'jquery'
|
||||
'underscore'
|
||||
'compiled/userSettings'
|
||||
'spin.js'
|
||||
'str/htmlEscape'
|
||||
# 'compiled/gradebook2/PostGradesDialog'
|
||||
'jsx/gradebook/SISGradePassback/PostGradesStore'
|
||||
'jsx/gradebook/SISGradePassback/PostGradesApp'
|
||||
'jst/gradebook2/column_header'
|
||||
'compiled/views/gradebook/SectionMenuView'
|
||||
'compiled/views/gradebook/GradingPeriodMenuView'
|
||||
'jsx/gradebook/grid/constants'
|
||||
'jsx/gradebook/grid/actions/gradebookToolbarActions'
|
||||
'jsx/gradebook/grid/actions/studentEnrollmentsActions'
|
||||
'jsx/gradebook/grid/stores/assignmentGroupsStore'
|
||||
'jsx/gradebook/grid/actions/assignmentGroupsActions'
|
||||
'compiled/gradebook2/AssignmentGroupWeightsDialog'
|
||||
'jsx/gradebook/grid/stores/sectionsStore'
|
||||
'jsx/gradebook/grid/actions/sectionsActions'
|
||||
'jsx/gradebook/grid/actions/tableActions'
|
||||
'jst/_avatar' #needed by row_student_name
|
||||
'jquery.ajaxJSON'
|
||||
'jquery.instructure_date_and_time'
|
||||
'jqueryui/dialog'
|
||||
'jqueryui/tooltip'
|
||||
'compiled/behaviors/tooltip'
|
||||
'jquery.instructure_misc_helpers'
|
||||
'jquery.instructure_misc_plugins'
|
||||
'vendor/jquery.ba-tinypubsub'
|
||||
'jqueryui/mouse'
|
||||
'jqueryui/position'
|
||||
'jqueryui/sortable'
|
||||
'compiled/jquery.kylemenu'
|
||||
'compiled/jquery/fixDialogButtons'
|
||||
'vendor/jquery.ba-tinypubsub'
|
||||
], (React, ReactDOM, round, InputFilterView, I18n, GRADEBOOK_TRANSLATIONS, $, _,
|
||||
userSettings, Spinner, htmlEscape, PostGradesStore, PostGradesApp,
|
||||
columnHeaderTemplate, SectionMenuView, GradingPeriodMenuView,
|
||||
GradebookConstants, GradebookToolbarActions, StudentEnrollmentsActions,
|
||||
AssignmentGroupsStore, AssignmentGroupActions, AssignmentGroupWeightsDialog,
|
||||
SectionsStore, SectionsActions, TableActions) ->
|
||||
|
||||
class Gradebook
|
||||
constructor: (@options) ->
|
||||
@initGradingPeriods()
|
||||
@initSections()
|
||||
@initPostGradesStore()
|
||||
@showPostGradesButton()
|
||||
@initSettingsDropdown()
|
||||
@initHeader()
|
||||
@attachSearchBarEventHandlers()
|
||||
@attachSetWeightsDialogHandlers()
|
||||
|
||||
initGradingPeriods: ->
|
||||
@gradingPeriods = @options.active_grading_periods
|
||||
|
||||
initSections: ->
|
||||
@sections = {}
|
||||
$.subscribe 'currentSection/change', @onSectionChange
|
||||
SectionsStore.listen (sectionStoreData) =>
|
||||
sections = sectionStoreData.sections
|
||||
@sectionToShow = sectionStoreData.selected
|
||||
@sections = {}
|
||||
if (sections != null && sections != undefined)
|
||||
for section in sections
|
||||
htmlEscape(section)
|
||||
@sections[section.id] = section
|
||||
|
||||
if sections.length > 2 # 2 because a filler "All sections" is inserted
|
||||
@drawSectionSelectButton(@sections)
|
||||
|
||||
|
||||
onSectionChange: (section) ->
|
||||
TableActions.enterLoadingState()
|
||||
SectionsActions.selectSection(section)
|
||||
@sectionToShow = section
|
||||
if @sectionToShow
|
||||
userSettings.contextSet('grading_show_only_section', @sectionToShow)
|
||||
else
|
||||
userSettings.contextRemove('grading_show_only_section', @sectionToShow)
|
||||
|
||||
initPostGradesStore: ->
|
||||
@postGradesStore = PostGradesStore
|
||||
course:
|
||||
id: @options.context_id
|
||||
sis_id: @options.context_sis_id
|
||||
|
||||
@postGradesStore.setSelectedSection @sectionToShow
|
||||
|
||||
showPostGradesButton: ->
|
||||
app = React.createElement(PostGradesApp, store: @postGradesStore)
|
||||
$placeholder = $('.post-grades-placeholder')
|
||||
if ($placeholder.length > 0)
|
||||
ReactDOM.render(app, $placeholder[0])
|
||||
|
||||
initSettingsDropdown: () ->
|
||||
preferences = @getInitialToolbarPreferences()
|
||||
@initCheckboxes(preferences)
|
||||
@initStudentNamesOption(preferences)
|
||||
@initArrangeByOption(preferences)
|
||||
@initNotesColumnOption(preferences)
|
||||
@attachSettingsDropdownEventHandlers(preferences)
|
||||
|
||||
sectionList: ->
|
||||
_.map @sections, (section, id) =>
|
||||
if(section.passback_status)
|
||||
date = new Date(section.passback_status.sis_post_grades_status.grades_posted_at)
|
||||
{ name: section.name, id: id, passback_status: section.passback_status, date: date, checked: @sectionToShow == id }
|
||||
|
||||
drawSectionSelectButton: (sections) ->
|
||||
@sectionMenu = new SectionMenuView(
|
||||
el: $('.section-button-placeholder'),
|
||||
sections: sections,
|
||||
course: {name: @options.course_name},
|
||||
showSections: sections,
|
||||
currentSection: @sectionToShow)
|
||||
@sectionMenu.render()
|
||||
|
||||
getInitialToolbarPreferences: () ->
|
||||
storedSortOrder = @options.gradebook_column_order_settings ||
|
||||
{ sortType: 'assignment_group' }
|
||||
savedPreferences =
|
||||
hideStudentNames: userSettings.contextGet('hideStudentNames')
|
||||
hideNotesColumn: !@options.teacher_notes || @options.teacher_notes.hidden
|
||||
showAttendanceColumns: userSettings.contextGet('showAttendanceColumns')
|
||||
showConcludedEnrollments: userSettings.contextGet('showConcludedEnrollments')
|
||||
showInactiveEnrollments: userSettings.contextGet('showInactiveEnrollments')
|
||||
arrangeBy: storedSortOrder.sortType
|
||||
_.defaults(savedPreferences, GradebookConstants.DEFAULT_TOOLBAR_PREFERENCES)
|
||||
|
||||
initCheckboxes: (preferences) ->
|
||||
$('#show_attendance').prop('checked', preferences.showAttendanceColumns)
|
||||
$('#show_concluded_enrollments').prop('checked', (
|
||||
@options.course_is_concluded || preferences.showConcludedEnrollments)
|
||||
)
|
||||
$('#show_inactive_enrollments').prop('checked', (
|
||||
@options.course_is_concluded || preferences.showInactiveEnrollments)
|
||||
)
|
||||
|
||||
initStudentNamesOption: (preferences) ->
|
||||
namesHidden = preferences.hideStudentNames
|
||||
if namesHidden then $('#student_names_toggle').addClass('hide_students')
|
||||
displayText = if namesHidden then I18n.t('Show Student Names') else I18n.t('Hide Student Names')
|
||||
$('#student_names_toggle').text(displayText)
|
||||
|
||||
initArrangeByOption: (preferences) ->
|
||||
$arrangeBy = $('#arrange_by_toggle')
|
||||
if preferences.arrangeBy == 'due_date'
|
||||
arrangeByData = 'due_date'
|
||||
displayText = I18n.t('Arrange Columns by Assignment Group')
|
||||
else
|
||||
arrangeByData = 'assignment_group'
|
||||
displayText = I18n.t('Arrange Columns by Due Date')
|
||||
|
||||
$arrangeBy.data('arrange_by', arrangeByData)
|
||||
$arrangeBy.text(displayText)
|
||||
|
||||
initNotesColumnOption: (preferences) ->
|
||||
notesHidden = preferences.hideNotesColumn
|
||||
if notesHidden then $('#notes_toggle').addClass('hide_notes')
|
||||
displayText = if notesHidden then I18n.t('Show Notes Column') else I18n.t('Hide Notes Column')
|
||||
$('#notes_toggle').text(displayText)
|
||||
|
||||
attachSettingsDropdownEventHandlers: () ->
|
||||
$('#student_names_toggle').click(@studentNamesToggle)
|
||||
$('#arrange_by_toggle').click(@arrangeByToggle)
|
||||
$('#notes_toggle').click(@notesToggle)
|
||||
$('#show_concluded_enrollments').change(@concludedEnrollmentsChange)
|
||||
$('#show_inactive_enrollments').change(@inactiveEnrollmentsChange)
|
||||
|
||||
attachSetWeightsDialogHandlers: () ->
|
||||
$.subscribe('assignment_group_weights_changed', @updateAssignmentGroupWeights)
|
||||
$('#set-group-weights').click @openSetAssignmentGroupWeightsDialog
|
||||
|
||||
updateAssignmentGroupWeights: (options) ->
|
||||
AssignmentGroupActions.replaceAssignmentGroups(options.assignmentGroups)
|
||||
|
||||
openSetAssignmentGroupWeightsDialog: () =>
|
||||
assignmentGroups = AssignmentGroupsStore.assignmentGroups.data
|
||||
params =
|
||||
context: @options
|
||||
assignmentGroups: assignmentGroups
|
||||
new AssignmentGroupWeightsDialog params
|
||||
|
||||
attachSearchBarEventHandlers: () ->
|
||||
$('.gradebook_filter').keyup ->
|
||||
searchTerm = $('#gradebook-filter-input').val()
|
||||
StudentEnrollmentsActions.search(searchTerm)
|
||||
|
||||
studentNamesToggle: (event) =>
|
||||
event.preventDefault()
|
||||
$studentNames = $(event.target)
|
||||
$studentNames.toggleClass('hide_students')
|
||||
hideStudents = $studentNames.hasClass('hide_students')
|
||||
displayText = if hideStudents then I18n.t('Show Student Names') else I18n.t('Hide Student Names')
|
||||
|
||||
$studentNames.text(displayText)
|
||||
GradebookToolbarActions.toggleStudentNames(hideStudents)
|
||||
|
||||
arrangeByToggle: (event) =>
|
||||
event.preventDefault()
|
||||
$arrangeBy = $(event.target)
|
||||
if $arrangeBy.data('arrange_by') == 'due_date'
|
||||
arrangeByData = 'assignment_group'
|
||||
displayText = I18n.t('Arrange Columns by Due Date')
|
||||
else
|
||||
arrangeByData = 'due_date'
|
||||
displayText = I18n.t('Arrange Columns by Assignment Group')
|
||||
|
||||
$arrangeBy.data('arrange_by', arrangeByData)
|
||||
$arrangeBy.text(displayText)
|
||||
GradebookToolbarActions.arrangeColumnsBy(arrangeByData)
|
||||
|
||||
notesToggle: (event) =>
|
||||
event.preventDefault()
|
||||
$notes = $(event.target)
|
||||
$notes.toggleClass('hide_notes')
|
||||
hideNotesColumn = $notes.hasClass('hide_notes')
|
||||
displayText = if hideNotesColumn then I18n.t('Show Notes Column') else I18n.t('Hide Notes Column')
|
||||
|
||||
$notes.text(displayText)
|
||||
GradebookToolbarActions.toggleNotesColumn(hideNotesColumn)
|
||||
|
||||
concludedEnrollmentsChange: () =>
|
||||
$showConcludedEnrollments = $('#show_concluded_enrollments')
|
||||
userSettings.contextSet 'showConcludedEnrollments', $showConcludedEnrollments.prop('checked')
|
||||
StudentEnrollmentsActions.load()
|
||||
|
||||
inactiveEnrollmentsChange: () =>
|
||||
$showInactiveEnrollments = $('#show_inactive_enrollments')
|
||||
userSettings.contextSet 'showInactiveEnrollments', $showInactiveEnrollments.prop('checked')
|
||||
StudentEnrollmentsActions.load()
|
||||
|
||||
initHeader: ->
|
||||
@gradingPeriodToShow = @getGradingPeriodToShow()
|
||||
@drawGradingPeriodSelectButton() if @options.multiple_grading_periods_enabled
|
||||
|
||||
$('#gradebook_settings').kyleMenu()
|
||||
$('#download_csv').kyleMenu()
|
||||
|
||||
drawGradingPeriodSelectButton: () ->
|
||||
@gradingPeriodMenu = new GradingPeriodMenuView(
|
||||
el: $('.multiple-grading-periods-selector-placeholder'),
|
||||
periods: @gradingPeriodList(),
|
||||
currentGradingPeriod: @gradingPeriodToShow)
|
||||
@gradingPeriodMenu.render()
|
||||
|
||||
gradingPeriodList: ->
|
||||
_.map @options.active_grading_periods, (period) =>
|
||||
{ title: period.title, id: period.id, checked: @gradingPeriodToShow == period.id }
|
||||
|
||||
getGradingPeriodToShow: () ->
|
||||
currentPeriodId = userSettings.contextGet('gradebook_current_grading_period')
|
||||
|
||||
if currentPeriodId && (@isAllGradingPeriods(currentPeriodId) || @gradingPeriodIsActive(currentPeriodId))
|
||||
currentPeriodId
|
||||
else
|
||||
ENV.GRADEBOOK_OPTIONS.current_grading_period_id
|
||||
|
||||
isAllGradingPeriods: (currentPeriodId) ->
|
||||
currentPeriodId == "0"
|
||||
|
||||
gradingPeriodIsActive: (gradingPeriodId) ->
|
||||
activePeriodIds = _.pluck(@gradingPeriods, 'id')
|
||||
_.contains(activePeriodIds, gradingPeriodId)
|
|
@ -1,18 +0,0 @@
|
|||
define [
|
||||
'compiled/util/round'
|
||||
], (round) ->
|
||||
GradeFormatter = (score, possibleScore, gradeAsPoints) ->
|
||||
@score = score
|
||||
@possibleScore = possibleScore
|
||||
@gradeAsPoints = gradeAsPoints
|
||||
|
||||
GradeFormatter.prototype.toString = () ->
|
||||
maxDecimals = round.DEFAULT
|
||||
percentGrade = @score / @possibleScore * 100
|
||||
|
||||
if (not @score || percentGrade == Infinity || isNaN(percentGrade))
|
||||
return '-'
|
||||
else
|
||||
return if @gradeAsPoints then @score else round(percentGrade, maxDecimals) + '%'
|
||||
|
||||
return GradeFormatter
|
|
@ -1,5 +1,4 @@
|
|||
define [
|
||||
'jsx/gradebook/grid/actions/gradingPeriodsActions'
|
||||
'compiled/userSettings'
|
||||
'i18n!gradebook2'
|
||||
'jquery'
|
||||
|
@ -8,7 +7,7 @@ define [
|
|||
'jst/gradebook2/grading_period_to_show_menu'
|
||||
'compiled/jquery.kylemenu'
|
||||
'vendor/jquery.ba-tinypubsub'
|
||||
], (GradingPeriodsActions, userSettings, I18n, $, _, {View}, template) ->
|
||||
], (userSettings, I18n, $, _, {View}, template) ->
|
||||
|
||||
class GradingPeriodMenuView extends View
|
||||
|
||||
|
@ -44,7 +43,6 @@ define [
|
|||
)
|
||||
|
||||
onGradingPeriodChange: (period) =>
|
||||
GradingPeriodsActions.select({ id: period }) if ENV.GRADEBOOK_OPTIONS.gradebook_performance_enabled
|
||||
@currentGradingPeriod = period
|
||||
@updateGradingPeriods()
|
||||
@storePeriodSetting period
|
||||
|
|
|
@ -6,8 +6,7 @@ define [
|
|||
'jst/gradebook2/section_to_show_menu'
|
||||
'compiled/jquery.kylemenu'
|
||||
'vendor/jquery.ba-tinypubsub'
|
||||
'jsx/gradebook/grid/actions/sectionsActions'
|
||||
], (I18n, $, _, {View}, template, SectionsActions) ->
|
||||
], (I18n, $, _, {View}, template) ->
|
||||
|
||||
class SectionMenuView extends View
|
||||
|
||||
|
|
|
@ -197,11 +197,10 @@ class GradebooksController < ApplicationController
|
|||
set_js_env
|
||||
@course_is_concluded = @context.completed?
|
||||
@post_grades_tools = post_grades_tools
|
||||
gradebook_version = @context.feature_enabled?(:gradebook_performance) ? :react_gradebook : :gradebook2
|
||||
|
||||
case @current_user.preferred_gradebook_version
|
||||
when "2"
|
||||
render gradebook_version
|
||||
render :gradebook2
|
||||
return
|
||||
when "srgb"
|
||||
render :screenreader
|
||||
|
@ -373,7 +372,6 @@ class GradebooksController < ApplicationController
|
|||
:gradebook_column_size_settings_url => change_gradebook_column_size_course_gradebook_url,
|
||||
:gradebook_column_order_settings => @current_user.preferences[:gradebook_column_order].try(:[], @context.id),
|
||||
:gradebook_column_order_settings_url => save_gradebook_column_order_course_gradebook_url,
|
||||
:gradebook_performance_enabled => @context.feature_enabled?(:gradebook_performance),
|
||||
:all_grading_periods_totals => @context.feature_enabled?(:all_grading_periods_totals),
|
||||
:sections => sections_json(@context.active_course_sections, @current_user, session),
|
||||
:settings_update_url => api_v1_course_gradebook_settings_update_url(@context),
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
define([
|
||||
'reflux',
|
||||
'jsx/gradebook/grid/constants',
|
||||
'jquery'
|
||||
], function (Reflux, GradebookConstants, $) {
|
||||
var AssignmentGroupsActions = Reflux.createActions({
|
||||
load: { asyncResult: true },
|
||||
replaceAssignmentGroups: { asyncResult: false },
|
||||
replaceAssignment: {asyncResult: false}
|
||||
});
|
||||
|
||||
AssignmentGroupsActions.load.listen(function() {
|
||||
var self = this;
|
||||
$.getJSON(GradebookConstants.assignment_groups_url)
|
||||
.done((json) => self.completed(json))
|
||||
.fail((jqxhr, textStatus, error) => self.failed(error));
|
||||
});
|
||||
|
||||
return AssignmentGroupsActions;
|
||||
});
|
|
@ -1,81 +0,0 @@
|
|||
define([
|
||||
'reflux',
|
||||
'underscore',
|
||||
'jsx/gradebook/grid/constants',
|
||||
'jsx/gradebook/grid/helpers/depaginator',
|
||||
'jquery',
|
||||
'i18n!gradebook2',
|
||||
'jquery.ajaxJSON'
|
||||
], function(Reflux, _, GradebookConstants, depaginate, $, I18n) {
|
||||
let CustomColumnsActions = Reflux.createActions({
|
||||
load: { asyncResult: true },
|
||||
loadColumnData: { asyncResult: true },
|
||||
loadTeacherNotes: { asyncResult: true }
|
||||
});
|
||||
|
||||
let onTeacherNotesCompleted = function(notesData) {
|
||||
GradebookConstants.teacher_notes = notesData;
|
||||
this.completed();
|
||||
};
|
||||
|
||||
let loadTeacherNotesFromServer = function() {
|
||||
let data, method, onComplete, url;
|
||||
|
||||
url = GradebookConstants.custom_columns_url;
|
||||
method = 'POST';
|
||||
data = {
|
||||
'column[title]': I18n.t('notes', 'Notes'),
|
||||
'column[position]': 1,
|
||||
'column[teacher_notes]': true
|
||||
};
|
||||
|
||||
$.ajaxJSON(url, method, data)
|
||||
.done(onTeacherNotesCompleted.bind(this));
|
||||
};
|
||||
|
||||
let loadTeacherNotesFromENV = function() {
|
||||
let teacherNotesUrl =
|
||||
GradebookConstants
|
||||
.custom_column_data_url
|
||||
.replace(/:id/, GradebookConstants.teacher_notes.id);
|
||||
|
||||
depaginate(teacherNotesUrl, { include_hidden: true })
|
||||
.then(this.completed);
|
||||
};
|
||||
|
||||
let loadColumnsData = function(columns) {
|
||||
_.each(columns, function(column) {
|
||||
if (!column.hidden) {
|
||||
CustomColumnsActions.loadColumnData(column.id);
|
||||
}
|
||||
|
||||
this.completed(columns);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
CustomColumnsActions.loadTeacherNotes.listen(function() {
|
||||
if (!GradebookConstants.teacher_notes) {
|
||||
(loadTeacherNotesFromServer.bind(this))();
|
||||
} else {
|
||||
(loadTeacherNotesFromENV.bind(this))();
|
||||
}
|
||||
});
|
||||
|
||||
CustomColumnsActions.load.listen(function() {
|
||||
$.getJSON(GradebookConstants.custom_columns_url)
|
||||
.done(loadColumnsData.bind(this))
|
||||
.fail(this.failed);
|
||||
});
|
||||
|
||||
CustomColumnsActions.loadColumnData.listen(function(columnId) {
|
||||
let url;
|
||||
|
||||
url = GradebookConstants.custom_column_data_url.replace(/:id/, columnId);
|
||||
|
||||
$.getJSON(url)
|
||||
.done(data => this.completed(data, columnId))
|
||||
.fail((jqxhr, textStatus, error) => this.failed(error));
|
||||
});
|
||||
|
||||
return CustomColumnsActions;
|
||||
});
|
|
@ -1,55 +0,0 @@
|
|||
define([
|
||||
'reflux',
|
||||
'jquery',
|
||||
'underscore',
|
||||
'i18n!gradebook2',
|
||||
'compiled/userSettings',
|
||||
'jsx/gradebook/grid/constants'
|
||||
], function (Reflux, $, _, I18n, userSettings, GradebookConstants) {
|
||||
|
||||
var GradebookToolbarActions = Reflux.createActions({
|
||||
toggleStudentNames: { asyncResult: false },
|
||||
toggleNotesColumn: { asyncResult: false },
|
||||
toggleTreatUngradedAsZero: { asyncResult: false },
|
||||
toggleTotalColumnInFront: { asyncResult: false },
|
||||
arrangeColumnsBy: { asyncResult: false },
|
||||
showTotalGradeAsPoints: { asyncResult: false },
|
||||
hideTotalDisplayWarning: { asyncResult: false }
|
||||
});
|
||||
|
||||
GradebookToolbarActions.toggleStudentNames.preEmit = (hideStudentNames) => {
|
||||
userSettings.contextSet('hideStudentNames', hideStudentNames);
|
||||
};
|
||||
|
||||
GradebookToolbarActions.toggleTreatUngradedAsZero.preEmit = (treatUngradedAsZero) => {
|
||||
userSettings.contextSet('treatUngradedAsZero', treatUngradedAsZero);
|
||||
};
|
||||
|
||||
GradebookToolbarActions.toggleTotalColumnInFront.preEmit = (totalColumnInFront) => {
|
||||
userSettings.contextSet('total_column_in_front', totalColumnInFront);
|
||||
};
|
||||
|
||||
GradebookToolbarActions.arrangeColumnsBy.preEmit = (criteria) => {
|
||||
var columnOrderUrl = GradebookConstants.gradebook_column_order_settings_url;
|
||||
var arrangeColumnsData = { column_order: { sortType: criteria } };
|
||||
$.ajaxJSON(columnOrderUrl, 'POST', arrangeColumnsData);
|
||||
};
|
||||
|
||||
GradebookToolbarActions.showTotalGradeAsPoints.preEmit = (showAsPoints) => {
|
||||
$.ajaxJSON(
|
||||
GradebookConstants.setting_update_url, "PUT",
|
||||
{ show_total_grade_as_points: showAsPoints }
|
||||
);
|
||||
};
|
||||
|
||||
GradebookToolbarActions.hideTotalDisplayWarning.preEmit = (hideWarning) => {
|
||||
userSettings.contextSet('warned_about_totals_display', hideWarning);
|
||||
};
|
||||
|
||||
GradebookToolbarActions.toggleNotesColumn.preEmit = (hideNotesColumn) => {
|
||||
var url = GradebookConstants.custom_column_url.replace(/:id/, GradebookConstants.teacher_notes.id);
|
||||
$.ajaxJSON(url, 'PUT', { 'column[hidden]': hideNotesColumn });
|
||||
};
|
||||
|
||||
return GradebookToolbarActions;
|
||||
});
|
|
@ -1,10 +0,0 @@
|
|||
define([
|
||||
'reflux',
|
||||
'jquery'
|
||||
], function (Reflux, $) {
|
||||
var GradingPeriodsActions = Reflux.createActions([
|
||||
'select'
|
||||
]);
|
||||
|
||||
return GradingPeriodsActions;
|
||||
});
|
|
@ -1,12 +0,0 @@
|
|||
define([
|
||||
'reflux',
|
||||
'jquery'
|
||||
], function (Reflux, $) {
|
||||
var KeyboardNavigationActions = Reflux.createActions([
|
||||
'setActiveCell',
|
||||
'constructKeyboardNavManager',
|
||||
'handleKeyboardEvent'
|
||||
]);
|
||||
|
||||
return KeyboardNavigationActions;
|
||||
});
|
|
@ -1,23 +0,0 @@
|
|||
define([
|
||||
'reflux',
|
||||
'jquery',
|
||||
'jsx/gradebook/grid/constants'
|
||||
], function (Reflux, $, GradebookConstants) {
|
||||
var SectionsActions = Reflux.createActions({
|
||||
load: {asyncResult: true},
|
||||
selectSection: {asyncResult: false}
|
||||
})
|
||||
|
||||
SectionsActions.load.listen(function() {
|
||||
var url, self;
|
||||
|
||||
self = this;
|
||||
url = GradebookConstants.sections_url;
|
||||
|
||||
$.getJSON(url)
|
||||
.then(this.completed)
|
||||
.fail((jqxhr, textStatus, error) => self.failed(error));
|
||||
});
|
||||
|
||||
return SectionsActions;
|
||||
});
|
|
@ -1,21 +0,0 @@
|
|||
define([
|
||||
'reflux',
|
||||
'jquery',
|
||||
'jsx/gradebook/grid/constants'
|
||||
], function (Reflux, $, GradebookConstants) {
|
||||
var SAVE_COLUMN_SIZE_URL = GradebookConstants.gradebook_column_size_settings_url;
|
||||
|
||||
var SettingsActions = Reflux.createActions([
|
||||
'resize',
|
||||
'saveColumnSize'
|
||||
]);
|
||||
|
||||
SettingsActions.saveColumnSize.preEmit = (newColumnWidth, dataKey) => {
|
||||
$.ajaxJSON(SAVE_COLUMN_SIZE_URL, 'POST', {
|
||||
column_id: dataKey,
|
||||
column_size: newColumnWidth
|
||||
});
|
||||
};
|
||||
|
||||
return SettingsActions;
|
||||
});
|
|
@ -1,36 +0,0 @@
|
|||
define([
|
||||
'reflux',
|
||||
'compiled/userSettings',
|
||||
'jsx/gradebook/grid/constants',
|
||||
'jsx/gradebook/grid/helpers/enrollmentsUrlHelper',
|
||||
'jsx/gradebook/grid/helpers/depaginator',
|
||||
], function (Reflux, UserSettings, GradebookConstants, EnrollmentsUrlHelper, depaginate) {
|
||||
|
||||
var StudentEnrollmentsActions = Reflux.createActions({
|
||||
load: { asyncResult: true },
|
||||
search: { asyncResult: false }
|
||||
});
|
||||
|
||||
function showConcluded() {
|
||||
return UserSettings.contextGet('showConcludedEnrollments') ||
|
||||
GradebookConstants.course_is_concluded;
|
||||
}
|
||||
|
||||
function showInactive() {
|
||||
return UserSettings.contextGet('showInactiveEnrollments');
|
||||
}
|
||||
|
||||
StudentEnrollmentsActions.load.listen(function() {
|
||||
var self = this,
|
||||
urlKey = EnrollmentsUrlHelper({
|
||||
showConcluded: showConcluded(),
|
||||
showInactive: showInactive()
|
||||
});
|
||||
|
||||
depaginate(GradebookConstants[urlKey])
|
||||
.then(self.completed)
|
||||
.fail((jqxhr, textStatus, error) => self.failed(error));
|
||||
});
|
||||
|
||||
return StudentEnrollmentsActions;
|
||||
});
|
|
@ -1,84 +0,0 @@
|
|||
define([
|
||||
'react',
|
||||
'reflux',
|
||||
'../constants',
|
||||
'jquery',
|
||||
'underscore',
|
||||
'vendor/jquery.ba-tinypubsub'
|
||||
], function (React, Reflux, GradebookConstants, $, _) {
|
||||
|
||||
var SubmissionsActions = Reflux.createActions({
|
||||
load: { asyncResult: true },
|
||||
updateGrade: { asyncResult: true },
|
||||
updatedSubmissionsReceived: { asyncResult: false }
|
||||
});
|
||||
|
||||
var splitUpIds = function(studentIds) {
|
||||
var splitIds, idSet;
|
||||
|
||||
splitIds = [];
|
||||
|
||||
while (studentIds.length > 0) {
|
||||
idSet = studentIds.splice(0, GradebookConstants.PAGINATION_COUNT);
|
||||
splitIds.push(idSet);
|
||||
}
|
||||
|
||||
return splitIds;
|
||||
};
|
||||
|
||||
SubmissionsActions.load.listen(function (studentIds) {
|
||||
var submissionsUrl, splitIds, deferreds, params;
|
||||
|
||||
submissionsUrl = GradebookConstants.submissions_url;
|
||||
splitIds = splitUpIds(studentIds);
|
||||
|
||||
deferreds = _.map(splitIds, function(idArray) {
|
||||
params = {};
|
||||
params.student_ids = idArray;
|
||||
params.response_fields = GradebookConstants.SUBMISSION_RESPONSE_FIELDS;
|
||||
return $.ajaxJSON(submissionsUrl, 'GET', params);
|
||||
});
|
||||
|
||||
$.when.apply($, deferreds).then(function() {
|
||||
var results, allResults, responses;
|
||||
allResults = [];
|
||||
|
||||
responses = arguments;
|
||||
if (responses.length > 1 && responses[1] === 'success') {
|
||||
responses = [responses];
|
||||
}
|
||||
// Orders responses so they're in the same order as requested
|
||||
responses = _.map(deferreds, (deferred) =>
|
||||
_.find(responses, function(result) {
|
||||
var isDeferred = (result[2] === deferred);
|
||||
return isDeferred;
|
||||
}
|
||||
));
|
||||
|
||||
for (var responseNumber = 0; responseNumber < responses.length; responseNumber++) {
|
||||
results = responses[responseNumber][0];
|
||||
allResults = allResults.concat(results);
|
||||
}
|
||||
|
||||
this.completed(allResults);
|
||||
}.bind(this));
|
||||
});
|
||||
|
||||
SubmissionsActions.updateGrade.listen(function (submission) {
|
||||
var url = GradebookConstants.change_grade_url
|
||||
.replace(':assignment', submission.assignmentId)
|
||||
.replace(':submission', submission.userId);
|
||||
|
||||
$.ajaxJSON(url, 'PUT', { 'submission[posted_grade]': submission.postedGrade })
|
||||
.done((response) => this.completed(submission, response))
|
||||
.fail((jqxhr, textStatus, error) => this.failed(error));
|
||||
});
|
||||
|
||||
$.subscribe('submissions_updated', function (updatedSubmissions) {
|
||||
if (updatedSubmissions.length > 0) {
|
||||
SubmissionsActions.updatedSubmissionsReceived(updatedSubmissions);
|
||||
}
|
||||
});
|
||||
|
||||
return SubmissionsActions;
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
define([
|
||||
'reflux',
|
||||
], function (Reflux) {
|
||||
var TableActions = Reflux.createActions([
|
||||
'enterLoadingState'
|
||||
]);
|
||||
|
||||
return TableActions;
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
define([
|
||||
'react',
|
||||
'react-dom',
|
||||
'./components/gradebook'
|
||||
], function (React, ReactDOM, Gradebook) {
|
||||
let MOUNT_ELEMENT = document.getElementById('gradebook_grid');
|
||||
ReactDOM.render(<Gradebook/>, MOUNT_ELEMENT);
|
||||
});
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
define([
|
||||
'react',
|
||||
'compiled/SubmissionDetailsDialog',
|
||||
'jsx/gradebook/grid/constants'
|
||||
], function (React, SubmissionDetailsDialog, GradebookConstants) {
|
||||
var GRADED = 'graded',
|
||||
ICON_CLASS = 'icon-',
|
||||
LATE_CLASS = 'late',
|
||||
RESUBMITTED_CLASS = 'resubmiited',
|
||||
SUBMISSION_TYPES = {
|
||||
discussion_topic: 'discussion',
|
||||
online_url: 'link',
|
||||
online_text_entry: 'text',
|
||||
online_upload: 'document',
|
||||
online_quiz: 'quiz',
|
||||
media_recording: 'media'
|
||||
};
|
||||
|
||||
var AssignmentGradeCell = React.createClass({
|
||||
propTypes: {
|
||||
activeCell: React.PropTypes.bool.isRequired,
|
||||
rowData: React.PropTypes.object.isRequired,
|
||||
renderer: React.PropTypes.func.isRequired,
|
||||
columnData: React.PropTypes.object
|
||||
},
|
||||
|
||||
hasIconDefined() {
|
||||
var submission = this.props.cellData;
|
||||
|
||||
if (!submission || submission.workflow_state === GRADED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return submission.submission_type in SUBMISSION_TYPES;
|
||||
},
|
||||
|
||||
shouldRenderIcon() {
|
||||
return this.hasIconDefined() && !this.props.activeCell;
|
||||
},
|
||||
|
||||
openDialog(assignment, student) {
|
||||
SubmissionDetailsDialog.open(assignment, student, GradebookConstants);
|
||||
},
|
||||
|
||||
handleSubmissionCommentClick(event) {
|
||||
var assignment, submission, student;
|
||||
|
||||
assignment = this.props.columnData.assignment;
|
||||
student = this.props.rowData.student;
|
||||
submission = this.props.cellData;
|
||||
|
||||
student["assignment_" + assignment.id] = submission;
|
||||
this.openDialog(assignment, student);
|
||||
},
|
||||
|
||||
renderIcon() {
|
||||
var submissionType = this.props.cellData.submission_type,
|
||||
className = ICON_CLASS + SUBMISSION_TYPES[submissionType];
|
||||
|
||||
return <i ref="icon" className={className}/>;
|
||||
},
|
||||
|
||||
renderGradeCell() {
|
||||
var Renderer = this.props.renderer;
|
||||
return <Renderer isActiveCell={this.props.activeCell}
|
||||
cellData={this.props.cellData}
|
||||
submission={this.props.cellData}
|
||||
columnData={this.props.columnData}
|
||||
rowData={this.props.rowData}/>;
|
||||
},
|
||||
|
||||
render() {
|
||||
var className, child;
|
||||
|
||||
className = "gradebook-cell-comment";
|
||||
child = this.shouldRenderIcon() ? this.renderIcon() : this.renderGradeCell();
|
||||
|
||||
if (this.props.activeCell) {
|
||||
className += ' active';
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="assignment-grade-cell">
|
||||
<a ref="detailsDialog"
|
||||
href="#"
|
||||
onClick={this.handleSubmissionCommentClick}
|
||||
className={className}>
|
||||
<span className="gradebook-cell-comment-label">
|
||||
I18n.t('submission comments')
|
||||
</span>
|
||||
</a>
|
||||
{child}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return AssignmentGradeCell;
|
||||
});
|
|
@ -1,67 +0,0 @@
|
|||
define([
|
||||
'reflux',
|
||||
'react',
|
||||
'compiled/grade_calculator',
|
||||
'compiled/gradebook2/gradeFormatter',
|
||||
'../../stores/gradebookToolbarStore',
|
||||
'../../stores/submissionsStore',
|
||||
'../../helpers/currentOrFinal',
|
||||
'underscore'
|
||||
], function (Reflux, React, GradeCalculator, GradeFormatter,
|
||||
GradebookToolbarStore, SubmissionsStore, currentOrFinal, _) {
|
||||
var AssignmentGroupColumn = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
cellData: React.PropTypes.any.isRequired,
|
||||
rowData: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
mixins: [
|
||||
Reflux.connect(GradebookToolbarStore, 'toolbarOptions')
|
||||
],
|
||||
|
||||
formatTitle(assignmentGroupScore) {
|
||||
return assignmentGroupScore.score + " / " + assignmentGroupScore.possible;
|
||||
},
|
||||
|
||||
render() {
|
||||
var assignmentGroupsIndex, submissions, assignmentGroups,
|
||||
assignmentGroupGradeData, assignmentGroup, gradeFormatter, toolbarOptions,
|
||||
groupSums, relevantAssignmentGroups, currentAssignmentGroup, assignmentGroupScore;
|
||||
|
||||
submissions = this.props.cellData.submissions;
|
||||
submissions = _.flatten(_.values(submissions));
|
||||
submissions = SubmissionsStore.submissionsInCurrentPeriod(submissions);
|
||||
|
||||
let assignmentGroupColumnId = this.props.cellData.columnId;
|
||||
assignmentGroups = this.props.rowData.assignmentGroups;
|
||||
currentAssignmentGroup = this.props.cellData.assignmentGroup;
|
||||
relevantAssignmentGroups = SubmissionsStore.assignmentGroupsForSubmissions(submissions, assignmentGroups);
|
||||
|
||||
toolbarOptions = this.state.toolbarOptions;
|
||||
|
||||
if (this.groupIsInCurrentPeriod(currentAssignmentGroup, relevantAssignmentGroups)) {
|
||||
assignmentGroupGradeData = GradeCalculator.calculate(submissions, assignmentGroups);
|
||||
groupSums = assignmentGroupGradeData.group_sums;
|
||||
groupSums = _.find(groupSums, sum => sum.group.columnId === assignmentGroupColumnId);
|
||||
assignmentGroupScore = groupSums[currentOrFinal(toolbarOptions)];
|
||||
} else {
|
||||
assignmentGroupScore = {score: 0, possible: 0};
|
||||
}
|
||||
|
||||
gradeFormatter = new GradeFormatter(assignmentGroupScore.score, assignmentGroupScore.possible, false);
|
||||
|
||||
return (
|
||||
<div className='assignment-group-grade' title={this.formatTitle(assignmentGroupScore)} ref='cell'>
|
||||
{ gradeFormatter.toString() }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
groupIsInCurrentPeriod(assignmentGroup, relevantAssignmentGroups) {
|
||||
return _.contains(relevantAssignmentGroups, assignmentGroup);
|
||||
}
|
||||
});
|
||||
|
||||
return AssignmentGroupColumn;
|
||||
});
|
|
@ -1,44 +0,0 @@
|
|||
define([
|
||||
'react',
|
||||
'../../mixins/gradeCellMixin',
|
||||
'../../mixins/standardGradeInputMixin',
|
||||
'../../mixins/standardCellFocusMixin',
|
||||
'../../mixins/standardRenderMixin'
|
||||
], function (
|
||||
React,
|
||||
GradeCellMixin,
|
||||
StandardGradeInputMixin,
|
||||
StandardCellFocusMixin,
|
||||
StandardRenderMixin
|
||||
) {
|
||||
|
||||
var AssignmentLetterGrade = React.createClass({
|
||||
mixins: [
|
||||
GradeCellMixin,
|
||||
StandardGradeInputMixin,
|
||||
StandardCellFocusMixin,
|
||||
StandardRenderMixin
|
||||
],
|
||||
|
||||
renderViewGrade() {
|
||||
var submission = this.props.cellData;
|
||||
if (submission && submission.grade) {
|
||||
var gradingType = this.props.cellData.grading_type,
|
||||
score = (gradingType == 'letter_grade') ? submission.score : '';
|
||||
return (
|
||||
<div ref="grade">
|
||||
{submission.grade}
|
||||
<span className="letter-grade-points">
|
||||
{score}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return <div className='grade' ref="grade">-</div>;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return AssignmentLetterGrade;
|
||||
|
||||
});
|
|
@ -1,68 +0,0 @@
|
|||
define([
|
||||
'react',
|
||||
'underscore',
|
||||
'../../mixins/gradeCellMixin',
|
||||
'../../actions/submissionsActions'
|
||||
], function (React, _, GradeCellMixin, SubmissionsActions) {
|
||||
|
||||
var GRADEBOOK_CHECKBOX_CLASS = 'gradebook-checkbox';
|
||||
var NEXT_GRADE_TYPE = {
|
||||
'' : 'complete',
|
||||
'complete' : 'incomplete',
|
||||
'incomplete': ''
|
||||
};
|
||||
|
||||
var AssignmentPassFail = React.createClass({
|
||||
mixins: [GradeCellMixin],
|
||||
|
||||
getCurrentGrade() {
|
||||
var previousGrade = (this.props.cellData) ? this.props.cellData.grade : null,
|
||||
grade;
|
||||
|
||||
if (this.state.gradeToPost || this.state.gradeToPost === "") {
|
||||
grade = this.state.gradeToPost;
|
||||
} else {
|
||||
grade = previousGrade;
|
||||
}
|
||||
|
||||
return grade;
|
||||
},
|
||||
|
||||
getClassName() {
|
||||
var className = 'grade';
|
||||
if (this.getCurrentGrade() || this.getCurrentGrade() === '' || this.props.isActiveCell) {
|
||||
className += ' ' + GRADEBOOK_CHECKBOX_CLASS + ' '
|
||||
+ GRADEBOOK_CHECKBOX_CLASS + '-'
|
||||
+ this.getCurrentGrade();
|
||||
}
|
||||
|
||||
if (this.props.isActiveCell) {
|
||||
className += ' editable';
|
||||
}
|
||||
|
||||
return className;
|
||||
},
|
||||
|
||||
handleClick() {
|
||||
var currentGrade = this.getCurrentGrade() || '',
|
||||
isActiveCell = this.props.isActiveCell,
|
||||
gradeToPost = (isActiveCell) ? NEXT_GRADE_TYPE[currentGrade]
|
||||
: currentGrade;
|
||||
|
||||
this.setState({gradeToPost: gradeToPost}, this.sendSubmission);
|
||||
},
|
||||
|
||||
render() {
|
||||
var cellContent = (!this.props.cellData || this.isSubmissionGradedAsNull()) ? '-' : '';
|
||||
return (
|
||||
<div style={{width: '100%', height: '100%'}} onClick={this.handleClick}>
|
||||
<div ref="grade" className={this.getClassName()}>
|
||||
{cellContent}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return AssignmentPassFail;
|
||||
});
|
|
@ -1,35 +0,0 @@
|
|||
define([
|
||||
'react',
|
||||
'../../actions/submissionsActions',
|
||||
'../../mixins/gradeCellMixin',
|
||||
'../../mixins/standardGradeInputMixin',
|
||||
'../../mixins/standardCellFocusMixin',
|
||||
'../../mixins/standardRenderMixin'
|
||||
], function (
|
||||
React,
|
||||
SubmissionsActions,
|
||||
GradeCellMixin,
|
||||
StandardGradeInputMixin,
|
||||
StandardCellFocusMixin,
|
||||
StandardRenderMixin
|
||||
) {
|
||||
|
||||
var AssignmentPercentage = React.createClass({
|
||||
mixins: [
|
||||
GradeCellMixin,
|
||||
StandardGradeInputMixin,
|
||||
StandardCellFocusMixin,
|
||||
StandardRenderMixin
|
||||
],
|
||||
|
||||
renderViewGrade() {
|
||||
return (
|
||||
<div className="grade" ref="grade">
|
||||
{this.getDisplayGrade().replace("%", "")}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return AssignmentPercentage;
|
||||
});
|
|
@ -1,52 +0,0 @@
|
|||
define([
|
||||
'react',
|
||||
'../../mixins/gradeCellMixin',
|
||||
'../../mixins/standardRenderMixin',
|
||||
'../../mixins/standardGradeInputMixin',
|
||||
'../../mixins/standardCellFocusMixin'
|
||||
], function (React, GradeCellMixin, StandardRenderMixin, StandardGradeInputMixin,
|
||||
StandardCellFocusMixin) {
|
||||
var AssignmentPoints = React.createClass({
|
||||
mixins: [
|
||||
GradeCellMixin,
|
||||
StandardRenderMixin,
|
||||
StandardCellFocusMixin
|
||||
],
|
||||
|
||||
handleOnChange(event) {
|
||||
this.setState({gradeToPost: event.target.value});
|
||||
},
|
||||
|
||||
renderEditGrade() {
|
||||
return (
|
||||
<div className="points-input">
|
||||
<div className="out-of-float">
|
||||
<input
|
||||
type="text"
|
||||
onChange={this.handleOnChange}
|
||||
className="grade out-of-grade"
|
||||
ref="gradeInput"
|
||||
value={this.state.gradeToPost || this.getDisplayGrade()}/>
|
||||
</div>
|
||||
<div className="out-of-float out-of-points">
|
||||
<span className="divider">/</span>
|
||||
<span ref="pointsPossible">
|
||||
{this.props.columnData.assignment.points_possible}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
renderViewGrade() {
|
||||
return (
|
||||
<div className="grade" ref="grade">
|
||||
{this.getDisplayGrade()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return AssignmentPoints;
|
||||
});
|
|
@ -1,41 +0,0 @@
|
|||
define([
|
||||
'react',
|
||||
'reflux',
|
||||
'jsx/gradebook/grid/stores/customColumnsStore',
|
||||
'jsx/gradebook/grid/components/column_types/teacherNote'
|
||||
], function(React, Reflux, CustomColumnsStore, TeacherNote) {
|
||||
let CustomColumn = React.createClass({
|
||||
propTypes: {
|
||||
columnData: React.PropTypes.object.isRequired,
|
||||
rowData: React.PropTypes.object.isRequired,
|
||||
cellData: React.PropTypes.object
|
||||
},
|
||||
|
||||
content(columnDatum) {
|
||||
if (columnDatum === null || columnDatum === undefined) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return columnDatum.content;
|
||||
},
|
||||
|
||||
render() {
|
||||
let columnDatum, columnId, content, studentName, userId;
|
||||
|
||||
columnId = this.props.columnData.customColumnData.id;
|
||||
userId = this.props.rowData.student.user_id;
|
||||
columnDatum = this.props.cellData;
|
||||
content = this.content(columnDatum);
|
||||
studentName= this.props.rowData.student.user.name;
|
||||
|
||||
return (
|
||||
<TeacherNote
|
||||
key={'custom_columns-' + userId + '-' + columnId}
|
||||
note={content} userId={userId} studentName={studentName}
|
||||
columnId={columnId} />
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return CustomColumn;
|
||||
});
|
|
@ -1,148 +0,0 @@
|
|||
define([
|
||||
'react',
|
||||
'underscore',
|
||||
'i18n!gradebook',
|
||||
'jsx/gradebook/grid/constants',
|
||||
'jsx/gradebook/grid/components/dropdown_components/gradebookKyleMenu',
|
||||
'jquery',
|
||||
'jsx/gradebook/grid/components/dropdown_components/assignmentHeaderDropdownOptions',
|
||||
'jsx/gradebook/grid/components/dropdown_components/totalHeaderDropdownOptions',
|
||||
'jquery.instructure_date_and_time'
|
||||
], function(React, _, I18n, GradebookConstants, GradebookKyleMenu, $, AssignmentHeaderDropdownOptions, TotalHeaderDropdownOptions) {
|
||||
|
||||
var HeaderRenderer = React.createClass({
|
||||
propTypes: {
|
||||
label: React.PropTypes.string.isRequired,
|
||||
columnData: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
prettyDate(date) {
|
||||
return $.dateString(date, { localized: true });
|
||||
},
|
||||
|
||||
getHeaderDateFromOverrides(overrides) {
|
||||
var overrideWithDueAt;
|
||||
|
||||
if (overrides.length > 1) { return I18n.t('Multiple due dates'); }
|
||||
overrideWithDueAt = _.find(overrides, override => override.due_at);
|
||||
if (overrideWithDueAt) {
|
||||
return I18n.t('Due %{dueAt}', { dueAt: this.prettyDate(overrideWithDueAt.due_at) });
|
||||
} else {
|
||||
return I18n.t('No due date');
|
||||
}
|
||||
},
|
||||
|
||||
headerDate(columnData) {
|
||||
var assignment, dateContent;
|
||||
|
||||
assignment = columnData.assignment;
|
||||
|
||||
if (!assignment) {
|
||||
dateContent = undefined;
|
||||
} else if (assignment.due_at) {
|
||||
dateContent = I18n.t('Due %{dueAt}', { dueAt: this.prettyDate(assignment.due_at) });
|
||||
} else if (assignment.overrides) {
|
||||
dateContent = this.getHeaderDateFromOverrides(assignment.overrides);
|
||||
} else {
|
||||
dateContent = I18n.t('No due date');
|
||||
}
|
||||
|
||||
return dateContent;
|
||||
},
|
||||
|
||||
shouldDisplayAssignmentWarning() {
|
||||
var assignment = this.props.columnData.assignment;
|
||||
return assignment.shouldShowNoPointsWarning
|
||||
&& GradebookConstants.group_weighting_scheme === 'percent';
|
||||
},
|
||||
|
||||
getTitle() {
|
||||
if (this.shouldDisplayAssignmentWarning()) {
|
||||
return I18n.t('Assignments in this group have no points possible and cannot be included in grade calculation');
|
||||
}
|
||||
},
|
||||
|
||||
getWidth() {
|
||||
var paddingAdjustment = GradebookConstants.DEFAULT_LAYOUTS.headers.paddingAdjustment;
|
||||
return this.props.width - paddingAdjustment;
|
||||
},
|
||||
|
||||
label(columnData) {
|
||||
var assignment, label;
|
||||
|
||||
assignment = columnData.assignment;
|
||||
label = this.props.label;
|
||||
|
||||
if (assignment) {
|
||||
var className = 'assignment-name' + ((assignment.muted) ? ' muted' : '');
|
||||
return (
|
||||
<div title={label} className='gradebook-label' style={{width: this.getWidth()}}>
|
||||
<a className={className} href={assignment.html_url}>
|
||||
{ this.shouldDisplayAssignmentWarning() && <i ref='icon' title={this.getTitle()} className='icon-warning'></i> }
|
||||
{label}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div title={label} className='gradebook-label' style={{width: this.getWidth()}}>
|
||||
{label}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return label;
|
||||
},
|
||||
|
||||
renderDropdown(columnData) {
|
||||
let columnType = columnData.columnType,
|
||||
assignment = columnData.assignment,
|
||||
enrollments = columnData.enrollments,
|
||||
submissions = columnData.submissions,
|
||||
key, dropdownOptionsId;
|
||||
|
||||
if (assignment) {
|
||||
key = 'assignment-' + assignment.id;
|
||||
dropdownOptionsId = key + '-options';
|
||||
return (
|
||||
<GradebookKyleMenu key={key} dropdownOptionsId={dropdownOptionsId}
|
||||
idToAppendTo='gradebook_grid' screenreaderText={I18n.t('Assignment Options')}
|
||||
defaultClassNames='gradebook-header-drop' options={{ noButton: true }}>
|
||||
|
||||
<AssignmentHeaderDropdownOptions key={dropdownOptionsId}
|
||||
idAttribute={dropdownOptionsId} assignment={assignment}
|
||||
enrollments={enrollments} submissions={submissions}/>
|
||||
</GradebookKyleMenu>
|
||||
);
|
||||
} else if (columnType === 'total') {
|
||||
return (
|
||||
<GradebookKyleMenu key='total' dropdownOptionsId='total-options'
|
||||
idToAppendTo='gradebook_grid' screenreaderText={I18n.t('Total Column Options')}
|
||||
defaultClassNames='gradebook-header-drop' options={{ noButton: true }}>
|
||||
<TotalHeaderDropdownOptions key='total-options' idAttribute='total-options'/>
|
||||
</GradebookKyleMenu>
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
render() {
|
||||
let columnData = this.props.columnData,
|
||||
dueDate = this.headerDate(columnData),
|
||||
className = (dueDate) ? '' : ' title';
|
||||
|
||||
return (
|
||||
<div style={{width: this.getWidth()}}
|
||||
title={this.props.label}
|
||||
className={'gradebook-label gradebook-header-column' + className}>
|
||||
{this.label(columnData)}
|
||||
{this.renderDropdown(columnData)}
|
||||
<div title={dueDate} className='assignment-due-date' ref='dueDate'>
|
||||
{dueDate}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return HeaderRenderer;
|
||||
});
|
|
@ -1,28 +0,0 @@
|
|||
define([
|
||||
'react',
|
||||
'jsx/gradebook/grid/components/column_types/teacherNote',
|
||||
'jsx/gradebook/grid/constants'
|
||||
], function(React, TeacherNote, GradebookConstants) {
|
||||
|
||||
let NotesColumn = React.createClass({
|
||||
propTypes: {
|
||||
rowData: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
render() {
|
||||
let student = this.props.rowData.student,
|
||||
studentName = this.props.rowData.studentName,
|
||||
teacherNote = this.props.rowData.teacherNote,
|
||||
columnId = GradebookConstants.teacher_notes.id;
|
||||
|
||||
return (
|
||||
<TeacherNote
|
||||
key={'notes' + student.user_id} note={teacherNote}
|
||||
userId={student.user_id} studentName={studentName}
|
||||
columnId={columnId} />
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return NotesColumn;
|
||||
});
|
|
@ -1,73 +0,0 @@
|
|||
define([
|
||||
'react',
|
||||
'i18n!gradebook2',
|
||||
'reflux',
|
||||
'../../stores/gradebookToolbarStore',
|
||||
'../../constants'
|
||||
], function(React, I18n, Reflux, GradebookToolbarStore, GradebookConstants) {
|
||||
|
||||
let StudentNameColumn = React.createClass({
|
||||
mixins: [Reflux.connect(GradebookToolbarStore, "toolbarOptions")],
|
||||
|
||||
rowData() {
|
||||
return this.props.rowData;
|
||||
},
|
||||
|
||||
renderHiddenName() {
|
||||
const hiddenText = I18n.t('Hidden');
|
||||
return <span title={hiddenText}>{hiddenText}</span>;
|
||||
},
|
||||
|
||||
isConcludedOrInactive() {
|
||||
return this.isConcluded() || this.isInactive();
|
||||
},
|
||||
|
||||
isConcluded() {
|
||||
return this.rowData().isConcluded;
|
||||
},
|
||||
|
||||
isInactive() {
|
||||
return this.rowData().isInactive;
|
||||
},
|
||||
|
||||
renderEnrollmentStatus() {
|
||||
let enrollmentStatus;
|
||||
const labelTitle = I18n.t('This user is currently not able to access the course');
|
||||
|
||||
if(this.isConcluded()) {
|
||||
enrollmentStatus = I18n.t('concluded');
|
||||
} else if(this.isInactive()) {
|
||||
enrollmentStatus = I18n.t('inactive');
|
||||
}
|
||||
|
||||
return <span ref="enrollmentStatus" className='label'
|
||||
title={labelTitle}>{enrollmentStatus}</span>
|
||||
},
|
||||
|
||||
renderStudentName() {
|
||||
var displayName = this.rowData().studentName;
|
||||
|
||||
return <a ref="gradesUrl" title={displayName}
|
||||
href={this.rowData().student.grades.html_url}>{displayName}</a>
|
||||
},
|
||||
|
||||
renderHiddenOrStudentName() {
|
||||
var hideStudentNames = this.state.toolbarOptions.hideStudentNames;
|
||||
if(hideStudentNames) {
|
||||
return this.renderHiddenName();
|
||||
} else {
|
||||
return this.renderStudentName();
|
||||
}
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div ref="studentName" className="student-name">
|
||||
{this.renderHiddenOrStudentName()} {this.isConcludedOrInactive() ? this.renderEnrollmentStatus() : ''}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return StudentNameColumn;
|
||||
});
|
|
@ -1,150 +0,0 @@
|
|||
define([
|
||||
'react',
|
||||
'underscore',
|
||||
'jquery',
|
||||
'i18n!gradebook2',
|
||||
'reflux',
|
||||
'jsx/gradebook/grid/stores/gradebookToolbarStore',
|
||||
'jsx/gradebook/grid/actions/customColumnsActions',
|
||||
'react-modal',
|
||||
'jsx/gradebook/grid/constants',
|
||||
'compiled/gradebook2/GradebookHelpers',
|
||||
'compiled/jquery.rails_flash_notifications'
|
||||
], function(React, _, $, I18n, Reflux, GradebookToolbarStore,
|
||||
CustomColumnsActions, Modal, GradebookConstants, GradebookHelpers) {
|
||||
|
||||
const modalOverrides = {
|
||||
overlay : {
|
||||
backgroundColor: 'rgba(0,0,0,0.5)'
|
||||
},
|
||||
content : {
|
||||
position: 'static',
|
||||
top: '0',
|
||||
left: '0',
|
||||
right: 'auto',
|
||||
bottom: 'auto',
|
||||
borderRadius: '0',
|
||||
border: 'none',
|
||||
padding: '0'
|
||||
}
|
||||
};
|
||||
|
||||
var TeacherNote = React.createClass({
|
||||
propTypes: {
|
||||
note: React.PropTypes.string.isRequired,
|
||||
userId: React.PropTypes.string.isRequired,
|
||||
studentName: React.PropTypes.string.isRequired,
|
||||
columnId: React.PropTypes.string.isRequired
|
||||
},
|
||||
|
||||
mixins: [
|
||||
Reflux.connect(GradebookToolbarStore, 'toolbarOptions')
|
||||
],
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
showModal: false,
|
||||
content: this.props.note
|
||||
};
|
||||
},
|
||||
|
||||
showModal() {
|
||||
this.setState({ showModal: true });
|
||||
},
|
||||
|
||||
hideModal() {
|
||||
this.setState({ showModal: false, content: this.props.note });
|
||||
},
|
||||
|
||||
updateContent(event) {
|
||||
let newNote = event.target.value;
|
||||
if (GradebookHelpers.textareaIsLessThanOrEqualToMaxLength(newNote.length)) {
|
||||
this.setState({ content: newNote });
|
||||
} else if(GradebookHelpers.maxLengthErrorShouldBeShown(newNote.length)) {
|
||||
GradebookHelpers.flashMaxLengthError();
|
||||
}
|
||||
},
|
||||
|
||||
handleSubmit() {
|
||||
var columnId = this.props.columnId,
|
||||
regexReplace = columnId + '/data/' + this.props.userId,
|
||||
url = GradebookConstants.custom_column_datum_url.replace(/:id\/data\/:user_id/, regexReplace);
|
||||
|
||||
$.ajaxJSON(url, 'PUT', { 'column_data[content]': this.state.content },
|
||||
() => {
|
||||
this.setState({ showModal: false });
|
||||
},
|
||||
() => {
|
||||
$.flashError(I18n.t('There was an error saving the note. Please try again.'));
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
nameToDisplay() {
|
||||
var hideNames = this.state.toolbarOptions.hideStudentNames;
|
||||
return hideNames ? I18n.t('student (name hidden)') : this.props.studentName;
|
||||
},
|
||||
|
||||
render() {
|
||||
var close, save, title;
|
||||
|
||||
close = I18n.t('Close');
|
||||
title = I18n.t('Notes for %{name}', { name: this.nameToDisplay() });
|
||||
save = I18n.t('Save');
|
||||
|
||||
return (
|
||||
<div ref='noteCell' className='teacher-note' onClick={this.showModal}>
|
||||
<span>{this.state.content}</span>
|
||||
<Modal
|
||||
className='ReactModal__Content--canvas ReactModal__Content--mini-modal'
|
||||
overlayClassName='ReactModal__Overlay--canvas'
|
||||
style={modalOverrides}
|
||||
isOpen={this.state.showModal}
|
||||
onRequestClose={this.hideModal}>
|
||||
<div>
|
||||
<div className="ReactModal__Layout">
|
||||
<div className="ReactModal__InnerSection ReactModal__Header">
|
||||
<div className="ReactModal__Header-Title">
|
||||
<h4 ref='studentName'>
|
||||
{title}
|
||||
</h4>
|
||||
</div>
|
||||
<div className="ReactModal__Header-Actions">
|
||||
<button className="Button Button--icon-action"
|
||||
type="button" onClick={this.hideModal}>
|
||||
<i className="icon-x"></i>
|
||||
<span className="screenreader-only">
|
||||
{close}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="ReactModal__InnerSection ReactModal__Body notes-body"
|
||||
onClick={this.makeTextEditable}>
|
||||
<textarea className='notes-textarea'
|
||||
value={this.state.content} onChange={this.updateContent}/>
|
||||
</div>
|
||||
|
||||
<div className="ReactModal__InnerSection ReactModal__Footer">
|
||||
<div className="ReactModal__Footer-Actions">
|
||||
<button type="button" className="btn btn-default"
|
||||
onClick={this.hideModal}>
|
||||
{I18n.t('Cancel')}
|
||||
</button>
|
||||
<button type="submit" className="btn btn-primary"
|
||||
disabled={this.state.content === this.props.note}
|
||||
onClick={this.handleSubmit}>
|
||||
{save}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
return TeacherNote;
|
||||
});
|
|
@ -1,139 +0,0 @@
|
|||
define([
|
||||
'reflux',
|
||||
'react',
|
||||
'jquery',
|
||||
'underscore',
|
||||
'i18n!gradebook2',
|
||||
'compiled/grade_calculator',
|
||||
'jsx/gradebook/grid/constants',
|
||||
'compiled/gradebook2/gradeFormatter',
|
||||
'../../stores/gradebookToolbarStore',
|
||||
'../../stores/submissionsStore',
|
||||
'../../helpers/currentOrFinal',
|
||||
], function (Reflux, React, $, _, I18n, GradeCalculator, GRADEBOOK_CONSTANTS,
|
||||
GradeFormatter, GradebookToolbarStore, SubmissionsStore, currentOrFinal) {
|
||||
|
||||
function generateGroupHasNoPointsWarning(groupNames) {
|
||||
return I18n.t({
|
||||
one: 'Score does not include %{groups} because it has no points possible',
|
||||
other: 'Score does not include %{groups} because they have no points possible'
|
||||
}, {
|
||||
groups: $.toSentence(groupNames),
|
||||
count: groupNames.length // TODO: delete me?
|
||||
});
|
||||
}
|
||||
|
||||
var TotalColumn = React.createClass({
|
||||
propTypes: {
|
||||
rowData: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
mixins: [
|
||||
Reflux.connect(GradebookToolbarStore, 'toolbarOptions')
|
||||
],
|
||||
|
||||
assignments() {
|
||||
return _.flatten(_.pluck(this.assignmentGroups(), 'assignments'));
|
||||
},
|
||||
|
||||
visibleAssignments() {
|
||||
return _.reject(this.assignments(), (a) =>
|
||||
_.contains(a.submission_types, 'not_graded'));
|
||||
},
|
||||
|
||||
getWarning() {
|
||||
let result = '';
|
||||
|
||||
if (this.anyMutedAssignments()) {
|
||||
result = I18n.t("This grade differs from the student's view of the grade because some assignments are muted");
|
||||
} else if (ENV.GRADEBOOK_OPTIONS.group_weighting_scheme === 'percent') {
|
||||
const invalidGroups = _.filter(this.assignmentGroups(), group => group.shouldShowNoPointsWarning);
|
||||
|
||||
if (invalidGroups.length > 0) {
|
||||
const groupNames = _.pluck(invalidGroups, 'name');
|
||||
result = generateGroupHasNoPointsWarning(groupNames);
|
||||
}
|
||||
|
||||
} else if (this.noPointsPossible()) {
|
||||
result = I18n.t("Can't compute score until an assignment has points possible");
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
anyMutedAssignments() {
|
||||
return _.any(this.visibleAssignments(), (va) => va.muted);
|
||||
},
|
||||
|
||||
noPointsPossible() {
|
||||
const pointsPossible = _.inject(
|
||||
this.assignments(),
|
||||
(sum, a) => sum + (a.points_possible || 0), 0
|
||||
);
|
||||
|
||||
return pointsPossible === 0;
|
||||
},
|
||||
|
||||
iconClassNames() {
|
||||
let result = '';
|
||||
|
||||
if (this.anyMutedAssignments()) {
|
||||
result = 'icon-muted final-warning';
|
||||
} else if (this.noPointsPossible()) {
|
||||
result = 'icon-warning final-warning';
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
submissions() {
|
||||
return SubmissionsStore.submissionsInCurrentPeriod(_.flatten(_.values(this.props.rowData.submissions)));
|
||||
},
|
||||
|
||||
assignmentGroups() {
|
||||
return this.props.rowData.assignmentGroups;
|
||||
},
|
||||
|
||||
assignmentGroupsForSubmissions() {
|
||||
return SubmissionsStore.assignmentGroupsForSubmissions(
|
||||
this.submissions(),
|
||||
this.assignmentGroups()
|
||||
);
|
||||
},
|
||||
|
||||
totalGradeData() {
|
||||
const groupWeightingScheme = ENV.GRADEBOOK_OPTIONS.group_weighting_scheme;
|
||||
return GradeCalculator.calculate(
|
||||
this.submissions(),
|
||||
this.assignmentGroupsForSubmissions(),
|
||||
groupWeightingScheme
|
||||
);
|
||||
},
|
||||
|
||||
toolbarOptions() {
|
||||
return this.state.toolbarOptions;
|
||||
},
|
||||
|
||||
total() {
|
||||
return this.totalGradeData()[currentOrFinal(this.toolbarOptions())];
|
||||
},
|
||||
|
||||
gradeFormatter() {
|
||||
const showPoints = this.toolbarOptions().showTotalGradeAsPoints,
|
||||
score = this.total().score,
|
||||
possible = this.total().possible;
|
||||
return new GradeFormatter(score, possible, showPoints);
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div ref="cell" title={this.getWarning()}>
|
||||
<i ref="icon" className={this.iconClassNames()} />
|
||||
<span className="total-grade" ref="totalGrade">{ this.gradeFormatter().toString() }</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return TotalColumn;
|
||||
});
|
|
@ -1,102 +0,0 @@
|
|||
define([
|
||||
'react',
|
||||
'underscore',
|
||||
'i18n!gradebook',
|
||||
'jsx/gradebook/grid/components/dropdown_components/headerDropdownOption',
|
||||
'jsx/gradebook/grid/constants',
|
||||
'jsx/gradebook/grid/components/dropdown_components/setDefaultGradeOption',
|
||||
'jsx/gradebook/grid/components/dropdown_components/muteAssignmentOption',
|
||||
'jsx/gradebook/grid/components/dropdown_components/messageStudentsWhoOption'
|
||||
], function (React, _, I18n, HeaderDropdownOption, GradebookConstants, SetDefaultGradeOption, MuteAssignmentOption, MessageStudentsWhoOption) {
|
||||
|
||||
var AssignmentHeaderDropdownOptions = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
assignment: React.PropTypes.object.isRequired,
|
||||
submissions: React.PropTypes.object.isRequired,
|
||||
idAttribute: React.PropTypes.string.isRequired,
|
||||
enrollments: React.PropTypes.array.isRequired
|
||||
},
|
||||
|
||||
getDropdownOptions() {
|
||||
var assignment = this.props.assignment,
|
||||
assignmentUrl = assignment.html_url,
|
||||
downloadableSubmissions = ['online_upload', 'online_text_entry', 'online_url'],
|
||||
hasDownloadableSubmissions = _.any(_.intersection(downloadableSubmissions, assignment.submission_types)),
|
||||
dropdownOptions = [
|
||||
{ title: I18n.t('Assignment Details'), action: 'showAssignmentDetails', url: assignmentUrl },
|
||||
{ title: I18n.t('Message Students Who...'), action: 'messageStudentsWho' },
|
||||
{ action: 'setDefaultGrade' },
|
||||
{ title: I18n.t('Curve Grades'), action: 'curveGrades' }
|
||||
],
|
||||
downloadSubmissionsOption, reuploadSubmissionsOption, speedGraderUrl, speedGraderOption,
|
||||
muteAssignmentOption;
|
||||
|
||||
if (hasDownloadableSubmissions && assignment.has_submitted_submissions) {
|
||||
downloadSubmissionsOption = { title: I18n.t('Download Submissions'), action: 'downloadSubmissions' };
|
||||
dropdownOptions.push(downloadSubmissionsOption);
|
||||
}
|
||||
|
||||
if (GradebookConstants.gradebook_is_editable && assignment.submissions_downloads > 0 ) {
|
||||
reuploadSubmissionsOption = { title: I18n.t('Re-Upload Submissions'), action: 'reuploadSubmissions' };
|
||||
dropdownOptions.push(reuploadSubmissionsOption);
|
||||
}
|
||||
|
||||
if (GradebookConstants.speed_grader_enabled) {
|
||||
speedGraderUrl = assignment.speedgrader_url;
|
||||
speedGraderOption = { title: I18n.t('Speedgrader'), url: speedGraderUrl, action: 'openSpeedgrader' };
|
||||
dropdownOptions.splice(1, 0, speedGraderOption);
|
||||
}
|
||||
|
||||
return dropdownOptions;
|
||||
},
|
||||
|
||||
render() {
|
||||
var options = this.getDropdownOptions(),
|
||||
assignment = this.props.assignment,
|
||||
enrollments = this.props.enrollments,
|
||||
submissions = this.props.submissions,
|
||||
key;
|
||||
return (
|
||||
<ul id={this.props.idAttribute} className="gradebook-header-menu">
|
||||
{
|
||||
// this map is temporary and will go away. eventually we'll have a
|
||||
// renderer for each dropdown option. for now, we render a generic
|
||||
// HeaderDropDownOption for yet-to-be-implemented dropdown options.
|
||||
_.map(options, (listItem) => {
|
||||
key = listItem.action + '-' + assignment.id;
|
||||
if (listItem.action === 'setDefaultGrade') {
|
||||
return (
|
||||
<SetDefaultGradeOption
|
||||
key={key}
|
||||
assignment={assignment}
|
||||
enrollments={enrollments}
|
||||
contextId={GradebookConstants.context_id}/>
|
||||
);
|
||||
} else if (listItem.action === 'messageStudentsWho') {
|
||||
return (
|
||||
<MessageStudentsWhoOption
|
||||
key={key} title={listItem.title} assignment={assignment}
|
||||
enrollments={enrollments} submissions={submissions}/>
|
||||
);
|
||||
} else {
|
||||
return(
|
||||
<HeaderDropdownOption
|
||||
key={key} title={listItem.title}
|
||||
dataAction={listItem.action} url={listItem.url}
|
||||
ref={listItem.action}/>
|
||||
);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
<MuteAssignmentOption
|
||||
key={'muteAssignment-' + assignment.id}
|
||||
assignment={assignment}/>
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return AssignmentHeaderDropdownOptions;
|
||||
});
|
|
@ -1,34 +0,0 @@
|
|||
define([
|
||||
'react',
|
||||
'reflux',
|
||||
'i18n!gradebook',
|
||||
'jsx/gradebook/grid/stores/gradebookToolbarStore',
|
||||
'jsx/gradebook/grid/actions/gradebookToolbarActions',
|
||||
'jsx/gradebook/grid/components/dropdown_components/headerDropdownOption'
|
||||
], function (React, Reflux, I18n, GradebookToolbarStore, GradebookToolbarActions, HeaderDropdownOption) {
|
||||
|
||||
var CurrentOrFinalGradeToggle = React.createClass({
|
||||
mixins: [
|
||||
Reflux.connect(GradebookToolbarStore, 'toolbarOptions'),
|
||||
],
|
||||
|
||||
showFinalGrade() {
|
||||
return this.state.toolbarOptions.treatUngradedAsZero;
|
||||
},
|
||||
|
||||
toggle(event) {
|
||||
event.preventDefault();
|
||||
var showFinalGrade = !this.showFinalGrade();
|
||||
GradebookToolbarActions.toggleTreatUngradedAsZero(showFinalGrade);
|
||||
},
|
||||
|
||||
render() {
|
||||
var text = this.showFinalGrade() ? I18n.t('Show Current Grade') : I18n.t('Show Final Grade');
|
||||
return (
|
||||
<HeaderDropdownOption title={text} handleClick={this.toggle} ref='gradeToggle'/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return CurrentOrFinalGradeToggle;
|
||||
});
|
|
@ -1,61 +0,0 @@
|
|||
define([
|
||||
'react',
|
||||
'jquery',
|
||||
], function (React, $) {
|
||||
|
||||
var GradebookKyleMenu = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
dropdownOptionsId: React.PropTypes.string.isRequired,
|
||||
idToAppendTo: React.PropTypes.string.isRequired,
|
||||
screenreaderText: React.PropTypes.string.isRequired,
|
||||
defaultClassNames: React.PropTypes.string.isRequired,
|
||||
children: React.PropTypes.element.isRequired,
|
||||
options: React.PropTypes.object
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
return { showMenu: false, menuOpen: false };
|
||||
},
|
||||
|
||||
handleMenuPopup(event) {
|
||||
this.setState({ menuOpen: event.type === 'popupopen' });
|
||||
},
|
||||
|
||||
handleDropdownClick(event) {
|
||||
var $link = $(event.target);
|
||||
var idToAppendTo = '#' + this.props.idToAppendTo;
|
||||
var kyleMenuOptions = this.props.options || {};
|
||||
var menuId = '#' + this.props.children.props.idAttribute;
|
||||
this.setState({ showMenu: true }, function(){
|
||||
var $menu = $(menuId);
|
||||
$link.kyleMenu(kyleMenuOptions);
|
||||
$menu.appendTo(idToAppendTo).bind('popupopen popupclose', (event) => {
|
||||
this.handleMenuPopup(event);
|
||||
}).popup('open');
|
||||
});
|
||||
},
|
||||
|
||||
cssClassNames() {
|
||||
var classNames = this.props.defaultClassNames;
|
||||
if (this.state.menuOpen) classNames += ' ui-menu-trigger-menu-is-open';
|
||||
return classNames;
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<a className={this.cssClassNames()}
|
||||
href='#'
|
||||
ref='dropdownLink'
|
||||
onClick={this.handleDropdownClick}>
|
||||
{this.props.screenreaderText}
|
||||
</a>
|
||||
{this.state.showMenu && this.props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return GradebookKyleMenu;
|
||||
});
|
|
@ -1,30 +0,0 @@
|
|||
define([
|
||||
'react',
|
||||
'i18n!gradebook'
|
||||
], function (React, I18n) {
|
||||
|
||||
var HeaderDropdownOption = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
title: React.PropTypes.string.isRequired,
|
||||
dataAction: React.PropTypes.string,
|
||||
url: React.PropTypes.string,
|
||||
handleClick: React.PropTypes.func
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<li>
|
||||
<a data-action={this.props.dataAction}
|
||||
href={this.props.url || '#'}
|
||||
onClick={this.props.handleClick}
|
||||
ref='link'>
|
||||
{this.props.title}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return HeaderDropdownOption;
|
||||
});
|
|
@ -1,51 +0,0 @@
|
|||
define([
|
||||
'react',
|
||||
'underscore',
|
||||
'jsx/gradebook/grid/components/dropdown_components/headerDropdownOption',
|
||||
'jsx/gradebook/grid/helpers/submissionsHelper',
|
||||
'jsx/gradebook/grid/helpers/enrollmentsHelper',
|
||||
'timezone',
|
||||
'jsx/gradebook/grid/helpers/messageStudentsWhoHelper',
|
||||
'message_students'
|
||||
], function (React, _, HeaderDropdownOption, SubmissionsHelper, EnrollmentsHelper, tz, MessageStudentsWhoHelper) {
|
||||
|
||||
let MessageStudentsWhoOption = React.createClass({
|
||||
propTypes: {
|
||||
title: React.PropTypes.string.isRequired,
|
||||
assignment: React.PropTypes.object.isRequired,
|
||||
enrollments: React.PropTypes.array.isRequired,
|
||||
submissions: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
openDialog() {
|
||||
let studentsForAssignment = EnrollmentsHelper.studentsThatCanSeeAssignment(this.props.enrollments, this.props.assignment);
|
||||
let students = this.combineStudentsWithScores(studentsForAssignment);
|
||||
let settings = MessageStudentsWhoHelper.settings(this.props.assignment, students);
|
||||
messageStudents(settings);
|
||||
},
|
||||
|
||||
combineStudentsWithScores(students) {
|
||||
let submissions = SubmissionsHelper.submissionsForAssignment(this.props.submissions, this.props.assignment);
|
||||
return _.map(students, function(student, studentId) {
|
||||
let studentWithScore = _.extend({ score: null, submitted_at: null }, student);
|
||||
let submission = submissions[studentId];
|
||||
if (submission) {
|
||||
studentWithScore.score = submission.score;
|
||||
studentWithScore.submitted_at = tz.parse(submission.submitted_at);
|
||||
}
|
||||
return studentWithScore;
|
||||
});
|
||||
},
|
||||
|
||||
render() {
|
||||
return(
|
||||
<HeaderDropdownOption
|
||||
handleClick={this.openDialog}
|
||||
key={'messageStudentsWho' + this.props.assignment.id}
|
||||
title={this.props.title}/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return MessageStudentsWhoOption;
|
||||
});
|
|
@ -1,36 +0,0 @@
|
|||
define([
|
||||
'react',
|
||||
'reflux',
|
||||
'i18n!gradebook',
|
||||
'jsx/gradebook/grid/components/dropdown_components/headerDropdownOption',
|
||||
'jsx/gradebook/grid/stores/gradebookToolbarStore',
|
||||
'jsx/gradebook/grid/actions/gradebookToolbarActions'
|
||||
], function (React, Reflux, I18n, HeaderDropdownOption, GradebookToolbarStore, GradebookToolbarActions) {
|
||||
|
||||
var TO_END = I18n.t("Move to end"),
|
||||
TO_FRONT = I18n.t("Move to front");
|
||||
|
||||
var MoveTotalColumnToggle = React.createClass({
|
||||
mixins: [
|
||||
Reflux.connect(GradebookToolbarStore, 'toolbarOptions'),
|
||||
],
|
||||
|
||||
isTotalColumnInFront() {
|
||||
return this.state.toolbarOptions.totalColumnInFront;
|
||||
},
|
||||
|
||||
handleClick(event) {
|
||||
GradebookToolbarActions.toggleTotalColumnInFront(!this.isTotalColumnInFront());
|
||||
},
|
||||
|
||||
render() {
|
||||
var title = (this.isTotalColumnInFront()) ? TO_END : TO_FRONT;
|
||||
return <HeaderDropdownOption key="moveToFront"
|
||||
title={title}
|
||||
handleClick={this.handleClick}
|
||||
ref="moveToFront"/>
|
||||
}
|
||||
});
|
||||
|
||||
return MoveTotalColumnToggle;
|
||||
});
|
|
@ -1,60 +0,0 @@
|
|||
define([
|
||||
'react',
|
||||
'jquery',
|
||||
'reflux',
|
||||
'underscore',
|
||||
'i18n!gradebook',
|
||||
'jsx/gradebook/grid/components/dropdown_components/headerDropdownOption',
|
||||
'compiled/AssignmentMuter',
|
||||
'jsx/gradebook/grid/constants',
|
||||
'jsx/gradebook/grid/actions/assignmentGroupsActions'
|
||||
], function (
|
||||
React,
|
||||
$,
|
||||
Reflux,
|
||||
_,
|
||||
I18n,
|
||||
HeaderDropdownOption,
|
||||
AssignmentMuter,
|
||||
GradebookConstants,
|
||||
AssignmentGroupsActions
|
||||
) {
|
||||
|
||||
var MUTE = I18n.t('Mute Assignment'),
|
||||
UNMUTE = I18n.t('Unmute Assignment'),
|
||||
MUTING_EVENT = 'assignment_muting_toggled';
|
||||
|
||||
var MuteAssignmentOption = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
assignment: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
openDialog() {
|
||||
var assignment = this.props.assignment,
|
||||
contextUrl = GradebookConstants.context_url,
|
||||
options = {openDialogInstantly: true},
|
||||
id = assignment.id,
|
||||
url = contextUrl + "/assignments/" + id + "/mute";
|
||||
|
||||
new AssignmentMuter(null, assignment, url, null, options);
|
||||
|
||||
$.subscribe(MUTING_EVENT, (assignment) => {
|
||||
AssignmentGroupsActions.replaceAssignment(assignment);
|
||||
$.unsubscribe(MUTING_EVENT);
|
||||
});
|
||||
},
|
||||
|
||||
render() {
|
||||
var title = (this.props.assignment.muted) ? UNMUTE : MUTE;
|
||||
return(
|
||||
<HeaderDropdownOption
|
||||
handleClick={this.openDialog}
|
||||
key={'muteAssignment' + this.props.assignment.id}
|
||||
title={title}/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return MuteAssignmentOption;
|
||||
});
|
|
@ -1,56 +0,0 @@
|
|||
define([
|
||||
'react',
|
||||
'reflux',
|
||||
'i18n!gradebook',
|
||||
'jsx/gradebook/grid/components/dropdown_components/headerDropdownOption',
|
||||
'jsx/gradebook/grid/stores/gradebookToolbarStore',
|
||||
'jsx/gradebook/grid/actions/gradebookToolbarActions',
|
||||
'compiled/gradebook2/GradeDisplayWarningDialog',
|
||||
], function (React, Reflux, I18n, HeaderDropdownOption, GradebookToolbarStore, GradebookToolbarActions, GradeDisplayWarningDialog) {
|
||||
|
||||
var PointsOrPercentageToggle = React.createClass({
|
||||
propTypes: {},
|
||||
|
||||
mixins:[Reflux.connect(GradebookToolbarStore, 'toolbarOptions')],
|
||||
|
||||
totalShowingAsPoints() {
|
||||
return this.state.toolbarOptions.showTotalGradeAsPoints;
|
||||
},
|
||||
|
||||
toggle() {
|
||||
var showAsPoints = !this.totalShowingAsPoints();
|
||||
GradebookToolbarActions.showTotalGradeAsPoints(showAsPoints);
|
||||
},
|
||||
|
||||
toggleAndHideWarning() {
|
||||
this.toggle();
|
||||
GradebookToolbarActions.hideTotalDisplayWarning(true);
|
||||
},
|
||||
|
||||
changeTotalDisplay(event) {
|
||||
event.preventDefault();
|
||||
var showWarning = !this.state.toolbarOptions.warnedAboutTotalsDisplay,
|
||||
dialogOptions;
|
||||
if (showWarning) {
|
||||
dialogOptions = { showing_points: this.totalShowingAsPoints(),
|
||||
unchecked_save: this.toggle, checked_save: this.toggleAndHideWarning };
|
||||
new GradeDisplayWarningDialog(dialogOptions);
|
||||
} else {
|
||||
this.toggle();
|
||||
}
|
||||
},
|
||||
|
||||
render() {
|
||||
var title = this.totalShowingAsPoints() ?
|
||||
I18n.t('Switch to Percentage') : I18n.t('Switch to Points');
|
||||
return(
|
||||
<HeaderDropdownOption key='pointsOrPercentage'
|
||||
title={title} dataAction='pointsOrPercentage'
|
||||
handleClick={this.changeTotalDisplay}
|
||||
ref='dropdownOption'/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return PointsOrPercentageToggle;
|
||||
});
|
|
@ -1,55 +0,0 @@
|
|||
define([
|
||||
'react',
|
||||
'reflux',
|
||||
'underscore',
|
||||
'i18n!gradebook',
|
||||
'compiled/gradebook2/SetDefaultGradeDialog',
|
||||
'jsx/gradebook/grid/components/dropdown_components/headerDropdownOption'
|
||||
], function (React, Reflux, _, I18n, SetDefaultGradeDialog, HeaderDropdownOption) {
|
||||
|
||||
var SetDefaultGradeOption = React.createClass({
|
||||
|
||||
// TODO: make selectedSection isRequired in the ticket to filter by section
|
||||
// (or, even better, pre-filter the students we pass into SetDefaultGradeDialog
|
||||
// so we dont need to pass in a selectedSection)
|
||||
propTypes: {
|
||||
assignment: React.PropTypes.object.isRequired,
|
||||
enrollments: React.PropTypes.array.isRequired,
|
||||
contextId: React.PropTypes.string.isRequired,
|
||||
selectedSection: React.PropTypes.object
|
||||
},
|
||||
|
||||
students() {
|
||||
return _.pluck(this.props.enrollments, 'user');
|
||||
},
|
||||
|
||||
studentsThatCanSeeAssignment(students, assignment) {
|
||||
var studentIds = assignment.assignment_visibility;
|
||||
return _.filter(students, student => _.contains(studentIds, student.id));
|
||||
},
|
||||
|
||||
openDialog() {
|
||||
var assignment = this.props.assignment,
|
||||
students = this.studentsThatCanSeeAssignment(this.students(), assignment);
|
||||
// TODO: pass in a selectedSection once the ticket for section filtering is
|
||||
// implemented
|
||||
return new SetDefaultGradeDialog({
|
||||
assignment: assignment,
|
||||
students: students,
|
||||
selected_section: this.props.selectedSection,
|
||||
context_id: this.props.contextId
|
||||
});
|
||||
},
|
||||
|
||||
render() {
|
||||
return(
|
||||
<HeaderDropdownOption
|
||||
key={'setDefaultGrade-' + this.props.assignment.id}
|
||||
handleClick={this.openDialog}
|
||||
title={I18n.t('Set Default Grade')}/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return SetDefaultGradeOption;
|
||||
});
|
|
@ -1,31 +0,0 @@
|
|||
define([
|
||||
'react',
|
||||
'i18n!gradebook',
|
||||
'jsx/gradebook/grid/components/dropdown_components/currentOrFinalGradeToggle',
|
||||
'jsx/gradebook/grid/components/dropdown_components/pointsOrPercentageToggle',
|
||||
'jsx/gradebook/grid/components/dropdown_components/moveTotalColumnToggle',
|
||||
'jsx/gradebook/grid/constants'
|
||||
], function (React, I18n, CurrentOrFinalGradeToggle, PointsOrPercentageToggle, MoveTotalColumnToggle, GradebookConstants) {
|
||||
|
||||
var TotalHeaderDropdownOptions = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
idAttribute: React.PropTypes.string.isRequired
|
||||
},
|
||||
|
||||
render() {
|
||||
var moveToFrontToggle = { title: I18n.t('Move to Front'), action: 'moveToFront' },
|
||||
showPointsToggle = GradebookConstants.group_weighting_scheme !== 'percent';
|
||||
return (
|
||||
<ul id={this.props.idAttribute} className="gradebook-header-menu">
|
||||
{showPointsToggle &&
|
||||
<PointsOrPercentageToggle key='pointsOrPercentageToggle' ref='switchToPoints'/>}
|
||||
<MoveTotalColumnToggle key='moveTotalColumn' ref='moveToFront'/>
|
||||
<CurrentOrFinalGradeToggle key='currentOrFinalToggle' ref='currentOrFinalToggle'/>
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return TotalHeaderDropdownOptions;
|
||||
});
|
|
@ -1,307 +0,0 @@
|
|||
define([
|
||||
'react',
|
||||
'fixed-data-table',
|
||||
'jquery',
|
||||
'underscore',
|
||||
'reflux',
|
||||
'i18n!gradebook2',
|
||||
'jsx/gradebook/grid/wrappers/columnFactory',
|
||||
'jsx/gradebook/grid/wrappers/headerWrapper',
|
||||
'jsx/gradebook/grid/constants',
|
||||
'jsx/gradebook/grid/actions/assignmentGroupsActions',
|
||||
'jsx/gradebook/grid/stores/settingsStore',
|
||||
'jsx/gradebook/grid/actions/settingsActions',
|
||||
'jsx/gradebook/grid/stores/gradebookToolbarStore',
|
||||
'jsx/gradebook/grid/stores/gradingPeriodsStore',
|
||||
'jsx/gradebook/grid/actions/studentEnrollmentsActions',
|
||||
'jsx/gradebook/grid/actions/submissionsActions',
|
||||
'jsx/gradebook/grid/actions/customColumnsActions',
|
||||
'jsx/gradebook/grid/stores/keyboardNavigationStore',
|
||||
'jsx/gradebook/grid/actions/keyboardNavigationActions',
|
||||
'jsx/gradebook/grid/stores/tableStore',
|
||||
'jsx/gradebook/grid/actions/sectionsActions',
|
||||
'jsx/gradebook/grid/helpers/columnArranger',
|
||||
'spin.js',
|
||||
'jsx/gradebook/grid/helpers/submissionsHelper'
|
||||
], function (
|
||||
React,
|
||||
FixedDataTable,
|
||||
$,
|
||||
_,
|
||||
Reflux,
|
||||
I18n,
|
||||
ColumnFactory,
|
||||
HeaderWrapper,
|
||||
GradebookConstants,
|
||||
AssignmentGroupsActions,
|
||||
SettingsStore,
|
||||
SettingsActions,
|
||||
GradebookToolbarStore,
|
||||
GradingPeriodsStore,
|
||||
StudentEnrollmentsActions,
|
||||
SubmissionsActions,
|
||||
CustomColumnsActions,
|
||||
KeyboardNavigationStore,
|
||||
KeyboardNavigationActions,
|
||||
TableStore,
|
||||
SectionsActions,
|
||||
ColumnArranger,
|
||||
Spinner,
|
||||
SubmissionsHelper
|
||||
){
|
||||
var Table = FixedDataTable.Table,
|
||||
Column = FixedDataTable.Column,
|
||||
isColumnResizing = false,
|
||||
spinner;
|
||||
|
||||
var Gradebook = React.createClass({
|
||||
mixins: [
|
||||
Reflux.connect(KeyboardNavigationStore, 'keyboardNav'),
|
||||
Reflux.connect(SettingsStore, 'settings'),
|
||||
Reflux.connect(GradebookToolbarStore, 'toolbarOptions'),
|
||||
Reflux.connect(TableStore, 'tableData')
|
||||
],
|
||||
|
||||
componentWillMount() {
|
||||
AssignmentGroupsActions.load();
|
||||
StudentEnrollmentsActions.load()
|
||||
.then((studentEnrollments) => {
|
||||
var studentIds = _.pluck(studentEnrollments, 'user_id');
|
||||
SubmissionsActions.load(studentIds);
|
||||
});
|
||||
SectionsActions.load();
|
||||
CustomColumnsActions.loadTeacherNotes();
|
||||
CustomColumnsActions.load();
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
SettingsActions.resize();
|
||||
$(window).resize(SettingsActions.resize);
|
||||
},
|
||||
|
||||
componentDidUpdate() {
|
||||
KeyboardNavigationActions.constructKeyboardNavManager();
|
||||
},
|
||||
|
||||
handleKeyDown(event) {
|
||||
var reactGradebook = document.getElementById('react-gradebook-canvas');
|
||||
var knownCodes = GradebookConstants.RECOGNIZED_KEYBOARD_CODES;
|
||||
if (_.contains(knownCodes, event.keyCode)) {
|
||||
event.nativeEvent.preventDefault();
|
||||
event.persist();
|
||||
KeyboardNavigationActions.handleKeyboardEvent(event);
|
||||
$(reactGradebook).focus();
|
||||
}
|
||||
},
|
||||
|
||||
assignments() {
|
||||
var arrangeBy, comparator, assignments;
|
||||
|
||||
arrangeBy = this.state.toolbarOptions.arrangeColumnsBy;
|
||||
comparator = ColumnArranger.getComparator(arrangeBy);
|
||||
assignments = _.chain(this.state.tableData.assignments.data)
|
||||
.filter(assignment =>
|
||||
GradingPeriodsStore.assignmentIsInPeriod(assignment, GradingPeriodsStore.selected()))
|
||||
.value();
|
||||
return assignments.sort(comparator);
|
||||
},
|
||||
|
||||
getColumnWidth(column) {
|
||||
var customWidths = this.state.settings.columnWidths,
|
||||
defaultWidth = GradebookConstants.DEFAULT_LAYOUTS.headers.width,
|
||||
width = (customWidths && customWidths[column]) || defaultWidth;
|
||||
|
||||
return parseInt(width);
|
||||
},
|
||||
|
||||
handleColumnResize(newColumnWidth, dataKey) {
|
||||
SettingsActions.saveColumnSize(newColumnWidth, dataKey);
|
||||
isColumnResizing = false;
|
||||
},
|
||||
|
||||
rowGetter(index) {
|
||||
return this.state.tableData.rows[index];
|
||||
},
|
||||
|
||||
isColumnFixed(columnType) {
|
||||
return columnType === GradebookConstants.STUDENT_COLUMN_ID
|
||||
|| columnType === GradebookConstants.SECONDARY_COLUMN_ID
|
||||
|| columnType === GradebookConstants.TOTAL_COLUMN_ID
|
||||
&& this.state.toolbarOptions.totalColumnInFront;
|
||||
},
|
||||
|
||||
renderColumn(columnName, columnType, columnId, cellDataGetter, assignment, customColumnData) {
|
||||
var columnIdentifier = columnId || columnType,
|
||||
columnWidth = this.getColumnWidth(columnIdentifier),
|
||||
enrollments = this.state.tableData.students,
|
||||
submissions = this.state.tableData.submissions,
|
||||
columnData = {
|
||||
columnType: columnType,
|
||||
activeCell: this.state.keyboardNav.currentCellIndex,
|
||||
setActiveCell: KeyboardNavigationActions.setActiveCell,
|
||||
assignment: assignment,
|
||||
enrollments: enrollments,
|
||||
submissions: submissions,
|
||||
customColumnData: customColumnData
|
||||
};
|
||||
|
||||
return (
|
||||
<Column
|
||||
label={columnName}
|
||||
fixed={this.isColumnFixed(columnType)}
|
||||
cellDataGetter={cellDataGetter}
|
||||
width={columnWidth}
|
||||
dataKey={columnIdentifier}
|
||||
columnData={columnData}
|
||||
headerRenderer={HeaderWrapper.getHeader}
|
||||
cellRenderer={ColumnFactory.getRenderer}
|
||||
isResizable={true}
|
||||
minWidth={90}
|
||||
key={columnIdentifier}/>
|
||||
);
|
||||
},
|
||||
|
||||
renderAssignmentGroupColumns(assignmentGroups) {
|
||||
var cellDataGetter;
|
||||
|
||||
cellDataGetter = function(columnId, rowData) {
|
||||
var assignmentGroups, assignmentGroup, submissions;
|
||||
|
||||
assignmentGroups = this.state.tableData.assignmentGroups;
|
||||
assignmentGroup = _.find(assignmentGroups, group => group.columnId === columnId);
|
||||
submissions = rowData.submissions;
|
||||
|
||||
return {
|
||||
assignmentGroup: assignmentGroup,
|
||||
submissions: submissions,
|
||||
columnId: columnId
|
||||
};
|
||||
}.bind(this);
|
||||
return _.map(assignmentGroups, (assignmentGroup, index) => {
|
||||
var columnId = assignmentGroup.columnId;
|
||||
|
||||
|
||||
return this.renderColumn(assignmentGroup.name,
|
||||
GradebookConstants.ASSIGNMENT_GROUP_COLUMN_ID,
|
||||
columnId, cellDataGetter);
|
||||
});
|
||||
},
|
||||
|
||||
renderAssignmentColumns(assignments) {
|
||||
var cellDataGetter;
|
||||
cellDataGetter = function(columnId, rowData) {
|
||||
var submissions, submission;
|
||||
|
||||
submissions = rowData.submissions[columnId];
|
||||
if (submissions && submissions.length > 0) {
|
||||
submission = submissions[0];
|
||||
}
|
||||
|
||||
return submission;
|
||||
}.bind(this);
|
||||
|
||||
return _.map(assignments, (assignment) => {
|
||||
var columnId = assignment.id;
|
||||
|
||||
return this.renderColumn(assignment.name, assignment.grading_type, columnId, cellDataGetter, assignment);
|
||||
});
|
||||
},
|
||||
|
||||
hasStoreErrorOccured() {
|
||||
return this.state.tableData.error;
|
||||
},
|
||||
|
||||
renderSpinner() {
|
||||
spinner = new Spinner();
|
||||
$(spinner.spin().el).css({
|
||||
opacity: 0.5,
|
||||
top: '55px',
|
||||
left: '50%'
|
||||
}).addClass('use-css-transitions-for-show-hide').appendTo('#main');
|
||||
},
|
||||
|
||||
removeSpinner() {
|
||||
if (spinner) {
|
||||
$(spinner.el).remove();
|
||||
spinner = null;
|
||||
}
|
||||
},
|
||||
|
||||
renderNotesColumn() {
|
||||
if (!this.state.toolbarOptions.hideNotesColumn) {
|
||||
return this.renderColumn(I18n.t('Notes'), GradebookConstants.NOTES_COLUMN_ID, 'notesColumn');
|
||||
}
|
||||
},
|
||||
|
||||
renderCustomColumns(customColumns) {
|
||||
let customColumnData, mapFunction;
|
||||
|
||||
customColumnData = customColumns.customColumns.data;
|
||||
mapFunction = function(customColumn) {
|
||||
var columnId, columnData;
|
||||
|
||||
columnId = 'customColumn_' + customColumn.id;
|
||||
columnData = this.state.tableData.customColumns.customColumns.columnData;
|
||||
return this.renderColumn(customColumn.title, GradebookConstants.CUSTOM_COLUMN_ID, columnId, (columnId, rowData) => columnData[customColumn.id][rowData.student.user_id], null, customColumn);
|
||||
};
|
||||
|
||||
return _.map(customColumnData, mapFunction.bind(this));
|
||||
},
|
||||
|
||||
renderAllColumns() {
|
||||
var arrangeBy, columns, comparator, showTotalInFront, total;
|
||||
|
||||
arrangeBy = this.state.toolbarOptions.arrangeColumnsBy;
|
||||
comparator = ColumnArranger.getComparator(arrangeBy);
|
||||
total = this.renderColumn(I18n.t('Total'), 'total');
|
||||
showTotalInFront = this.state.toolbarOptions.totalColumnInFront,
|
||||
columns = [
|
||||
this.renderColumn(I18n.t('Student Name'), GradebookConstants.STUDENT_COLUMN_ID),
|
||||
this.renderNotesColumn(),
|
||||
this.renderCustomColumns(this.state.tableData.customColumns),
|
||||
this.renderAssignmentColumns(_.flatten(_.values(this.state.tableData.assignments)).sort(comparator), this.state.tableData.submissions),
|
||||
this.renderAssignmentGroupColumns(this.state.tableData.assignmentGroups),
|
||||
];
|
||||
|
||||
(showTotalInFront) ? columns.splice(1, 0, total) : columns.push(total);
|
||||
|
||||
return columns;
|
||||
},
|
||||
|
||||
render() {
|
||||
if (this.hasStoreErrorOccured()) {
|
||||
$.flashError(I18n.t('There was a problem loading the gradebook.'));
|
||||
}
|
||||
else if (!this.state.tableData.loading) {
|
||||
this.removeSpinner();
|
||||
return (
|
||||
<div id="react-gradebook-canvas"
|
||||
onKeyDown={this.handleKeyDown}
|
||||
tabIndex="0">
|
||||
<Table
|
||||
rowGetter={this.rowGetter}
|
||||
rowsCount={this.state.tableData.students.length}
|
||||
scrollToColumn={this.state.keyboardNav.currentColumnIndex}
|
||||
scrollToRow={this.state.keyboardNav.currentRowIndex}
|
||||
onColumnResizeEndCallback={this.handleColumnResize}
|
||||
isColumnResizing={isColumnResizing}
|
||||
rowHeight={GradebookConstants.DEFAULT_LAYOUTS.rows.height}
|
||||
height={this.state.settings.height}
|
||||
width={this.state.settings.width}
|
||||
headerHeight={GradebookConstants.DEFAULT_LAYOUTS.headers.height}>
|
||||
{this.renderAllColumns()}
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
if (!spinner) {
|
||||
this.renderSpinner();
|
||||
}
|
||||
|
||||
return <div/>;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return Gradebook;
|
||||
});
|
|
@ -1,114 +0,0 @@
|
|||
define([
|
||||
'react',
|
||||
'underscore',
|
||||
'./assignmentGradeCell'
|
||||
], function (React, _, AssignmentGradeCell) {
|
||||
|
||||
var GRADEBOOK_CELL_CLASS = 'gradebook-cell',
|
||||
ACTIVE_CLASS = ' active',
|
||||
LATE_CLASS = ' late',
|
||||
RESUBMIITED_CLASS = ' resubmitted',
|
||||
CONCLUDED_OR_INACTIVE_CLASS = ' grayed-out',
|
||||
ASSIGNMENT_TYPES = [
|
||||
'percent',
|
||||
'pass_fail',
|
||||
'letter_grade',
|
||||
'points',
|
||||
'gpa_scale',
|
||||
];
|
||||
|
||||
var GridCell = React.createClass({
|
||||
propTypes: {
|
||||
activeCell: React.PropTypes.number.isRequired,
|
||||
cellData: React.PropTypes.any,
|
||||
cellIndex: React.PropTypes.number.isRequired,
|
||||
columnData: React.PropTypes.object,
|
||||
rowData: React.PropTypes.object.isRequired,
|
||||
renderer: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
cellIndex: this.props.cellIndex
|
||||
};
|
||||
},
|
||||
|
||||
handleClick() {
|
||||
this.props.setActiveCell(this.state.cellIndex);
|
||||
},
|
||||
|
||||
isAssignment(columnData) {
|
||||
return _.contains(ASSIGNMENT_TYPES, columnData.columnType);
|
||||
},
|
||||
|
||||
isConcluded() {
|
||||
return this.props.rowData.isConcluded;
|
||||
},
|
||||
|
||||
isInactive() {
|
||||
return this.props.rowData.isInactive;
|
||||
},
|
||||
|
||||
isConcludedOrInactive() {
|
||||
return this.isConcluded() || this.isInactive();
|
||||
},
|
||||
|
||||
getClassName(isActiveCell, cellData, isAssignment) {
|
||||
var className = GRADEBOOK_CELL_CLASS;
|
||||
|
||||
if (isActiveCell) {
|
||||
className += ACTIVE_CLASS;
|
||||
}
|
||||
|
||||
if (isAssignment && cellData) {
|
||||
if (cellData.late) {
|
||||
className += LATE_CLASS;
|
||||
} else if (cellData.grade_matches_current_submission !== null && !(cellData.grade_matches_current_submission)) {
|
||||
className += RESUBMIITED_CLASS;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isConcludedOrInactive()) {
|
||||
className += CONCLUDED_OR_INACTIVE_CLASS;;
|
||||
}
|
||||
|
||||
return className;
|
||||
},
|
||||
|
||||
renderAssignmentCell(Renderer, isActiveCell) {
|
||||
return (<AssignmentGradeCell
|
||||
activeCell={isActiveCell}
|
||||
cellData={this.props.cellData}
|
||||
columnData={this.props.columnData}
|
||||
renderer={Renderer}
|
||||
rowData={this.props.rowData} />);
|
||||
},
|
||||
|
||||
renderGenericCell(Renderer, isActiveCell) {
|
||||
return (<Renderer isActiveCell={isActiveCell}
|
||||
cellData={this.props.cellData}
|
||||
columnData={this.props.columnData}
|
||||
rowData={this.props.rowData} />);
|
||||
},
|
||||
|
||||
render() {
|
||||
var Renderer = this.props.renderer,
|
||||
className = GRADEBOOK_CELL_CLASS,
|
||||
isAssignmentCell = this.isAssignment(this.props.columnData),
|
||||
isActiveCell = this.props.activeCell === this.state.cellIndex,
|
||||
renderCell = (isAssignmentCell) ? this.renderAssignmentCell : this.renderGenericCell,
|
||||
submission = this.props.cellData;
|
||||
|
||||
return (
|
||||
<div className={this.getClassName(isActiveCell, submission, isAssignmentCell)}
|
||||
onKeyDown={this.handleKeyPress}
|
||||
onClick={this.handleClick}
|
||||
key='null'>
|
||||
{renderCell(Renderer, isActiveCell)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return GridCell;
|
||||
});
|
|
@ -1,58 +0,0 @@
|
|||
define([
|
||||
'underscore'
|
||||
], function (_) {
|
||||
var GRADEBOOK_CONSTANTS = {
|
||||
STUDENT_COLUMN_ID: 'student',
|
||||
NOTES_COLUMN_ID: 'notes',
|
||||
PERCENT_COLUMN_ID: 'percent',
|
||||
PASS_FAIL_COLUMN_ID: 'pass_fail',
|
||||
LETTER_GRADE_COLUMN_ID: 'letter_grade',
|
||||
POINTS_COLUMN_ID: 'points',
|
||||
GPA_SCALE_COLUMN_ID: 'gpa_scale',
|
||||
TOTAL_COLUMN_ID: 'total',
|
||||
CUSTOM_COLUMN_ID: 'custom',
|
||||
ASSIGNMENT_GROUP_COLUMN_ID: 'assignment_group',
|
||||
MOUNT_ELEMENT: document.getElementById('gradebook-grid-wrapper'),
|
||||
DEFAULT_LAYOUTS: {
|
||||
headers: { width: 150, height: 40, flexGrow: 0, paddingAdjustment: 20 },
|
||||
rows: { height: 36 }
|
||||
},
|
||||
SUBMISSION_RESPONSE_FIELDS: [
|
||||
'id',
|
||||
'user_id',
|
||||
'url',
|
||||
'score',
|
||||
'grade',
|
||||
'submission_type',
|
||||
'submitted_at',
|
||||
'assignment_id',
|
||||
'grade_matches_current_submission',
|
||||
'attachments',
|
||||
'late',
|
||||
'workflow_state'
|
||||
],
|
||||
DEFAULT_TOOLBAR_PREFERENCES: {
|
||||
hideStudentNames: false,
|
||||
hideNotesColumn: true,
|
||||
treatUngradedAsZero: false,
|
||||
totalColumnInFront: false,
|
||||
arrangeColumnsBy: 'assignment_group',
|
||||
warnedAboutTotalsDisplay: false,
|
||||
showTotalGradeAsPoints: false
|
||||
},
|
||||
ASSIGNMENT_DATES: ['created_at', 'updated_at', 'due_at', 'lock_at', 'unlock_at'],
|
||||
OVERRIDE_DATES: ['all_day_date', 'due_at', 'lock_at', 'unlock_at'],
|
||||
PAGINATION_COUNT: 50,
|
||||
MAX_NOTE_LENGTH: 255,
|
||||
// keyboard codes: tab, enter, left arrow, up arrow, right arrow, down arrow
|
||||
RECOGNIZED_KEYBOARD_CODES: [9,13,37,38,39,40],
|
||||
refresh: function() {
|
||||
// For testing
|
||||
_.extend(this, ENV.GRADEBOOK_OPTIONS);
|
||||
}
|
||||
};
|
||||
|
||||
var CONSTANTS = _.extend({}, GRADEBOOK_CONSTANTS, ENV.GRADEBOOK_OPTIONS);
|
||||
|
||||
return CONSTANTS;
|
||||
});
|
|
@ -1,60 +0,0 @@
|
|||
define([
|
||||
'underscore'
|
||||
], function (_) {
|
||||
var ColumnArranger = {
|
||||
compareByDueDate: function(a, b) {
|
||||
var aDate = this.getDueDateFromAssignment(a);
|
||||
var bDate = this.getDueDateFromAssignment(b);
|
||||
var aDateIsNull = _.isNull(aDate);
|
||||
var bDateIsNull = _.isNull(bDate);
|
||||
if (aDateIsNull && !bDateIsNull) return 1;
|
||||
if (!aDateIsNull && bDateIsNull) return -1;
|
||||
if (aDateIsNull && bDateIsNull) {
|
||||
if (this.hasMultipleDueDates(a) && !this.hasMultipleDueDates(b)) return -1;
|
||||
if (!this.hasMultipleDueDates(a) && this.hasMultipleDueDates(b)) return 1;
|
||||
}
|
||||
aDate = +aDate;
|
||||
bDate = +bDate;
|
||||
if (aDate === bDate) {
|
||||
var aName = a.name.toLowerCase();
|
||||
var bName = b.name.toLowerCase();
|
||||
if (aName === bName) return 0;
|
||||
return aName > bName ? 1 : -1;
|
||||
}
|
||||
return aDate - bDate;
|
||||
},
|
||||
|
||||
hasMultipleDueDates: function(assignment) {
|
||||
return !!(
|
||||
assignment.has_overrides &&
|
||||
assignment.overrides &&
|
||||
assignment.overrides.length > 1
|
||||
);
|
||||
},
|
||||
|
||||
getDueDateFromAssignment: function(assignment) {
|
||||
if (assignment.due_at) return new Date(assignment.due_at);
|
||||
var overrides = assignment.overrides;
|
||||
if (!overrides || overrides.length > 1) return null;
|
||||
var overrideWithDueAt = _.find(overrides, override => override.due_at);
|
||||
return overrideWithDueAt ? new Date(overrideWithDueAt.due_at) : null;
|
||||
},
|
||||
|
||||
compareByAssignmentGroup: function(a, b) {
|
||||
var diffOfAssignmentGroupPosition = a.assignment_group_position - b.assignment_group_position;
|
||||
if (diffOfAssignmentGroupPosition === 0) {
|
||||
var diffOfAssignmentPosition = a.position - b.position;
|
||||
if (diffOfAssignmentPosition === 0) return 0;
|
||||
return diffOfAssignmentPosition;
|
||||
}
|
||||
return diffOfAssignmentGroupPosition;
|
||||
},
|
||||
|
||||
getComparator: function(arrangeBy) {
|
||||
if (arrangeBy === 'due_date') return this.compareByDueDate.bind(this);
|
||||
if (arrangeBy === 'assignment_group') return this.compareByAssignmentGroup.bind(this);
|
||||
}
|
||||
};
|
||||
|
||||
return ColumnArranger;
|
||||
});
|
|
@ -1,11 +0,0 @@
|
|||
define([], function() {
|
||||
var currentOrFinal = function(toolbarOptions) {
|
||||
if (toolbarOptions.treatUngradedAsZero) {
|
||||
return 'final';
|
||||
}
|
||||
|
||||
return 'current';
|
||||
};
|
||||
|
||||
return currentOrFinal;
|
||||
})
|
|
@ -1,86 +0,0 @@
|
|||
define([
|
||||
'jquery',
|
||||
'underscore',
|
||||
'jquery.ajaxJSON'
|
||||
], function($, _) {
|
||||
let exists, Depaginator, depaginate;
|
||||
|
||||
depaginate = function(url, data) {
|
||||
let deferred, depaginator;
|
||||
deferred = $.Deferred();
|
||||
depaginator = new Depaginator(url, deferred, data);
|
||||
depaginator.retrieve();
|
||||
|
||||
return deferred.promise();
|
||||
};
|
||||
|
||||
exists = function(object) {
|
||||
return object !== null && object !== undefined;
|
||||
};
|
||||
|
||||
let notExists = function(object) {
|
||||
return !exists(object);
|
||||
}
|
||||
|
||||
Depaginator = function(url, deferred, data) {
|
||||
this.url = url;
|
||||
this.deferred = deferred;
|
||||
this.data = data;
|
||||
};
|
||||
|
||||
Depaginator.prototype.retrieve = function() {
|
||||
return $.getJSON(this.url, this.data)
|
||||
.done(this.handleInitialRequest.bind(this));
|
||||
};
|
||||
|
||||
Depaginator.prototype.handleInitialRequest = function(resultData, status, xhr) {
|
||||
let paginationLinks, lastLink;
|
||||
paginationLinks = xhr.getResponseHeader('Link');
|
||||
lastLink = paginationLinks.match(/<[^>]+>; *rel="last"/);
|
||||
lastLink = lastLink[0].match(/<[^>]+>;/);
|
||||
let currentLink = paginationLinks.match(/<[^>]+>; *rel="current"/);
|
||||
currentLink = currentLink[0].match(/<[^>]+>;/);
|
||||
|
||||
if (notExists(lastLink) || lastLink[0] === currentLink[0]) {
|
||||
this.deferred.resolve(resultData);
|
||||
} else {
|
||||
this.depaginate(resultData, lastLink);
|
||||
}
|
||||
};
|
||||
|
||||
Depaginator.prototype.depaginate = function(firstPageData, lastLink) {
|
||||
let lastPage, requests;
|
||||
lastPage = lastLink[0].match(/page=(\d+)/)[1];
|
||||
lastPage = parseInt(lastPage, 10);
|
||||
requests = this.getAllRequests(lastPage);
|
||||
|
||||
this.makeRequests(requests).then(function() {
|
||||
let allOtherPagesData = _.chain(arguments)
|
||||
.map(response => response[0])
|
||||
.flatten()
|
||||
.value();
|
||||
let allData = firstPageData.concat(allOtherPagesData);
|
||||
this.deferred.resolve(allData);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Depaginator.prototype.getAllRequests = function(lastPage) {
|
||||
let requests = [], pageNumber, request;
|
||||
for (pageNumber = 2; pageNumber <= lastPage; pageNumber++) {
|
||||
request = this.fetchResources(pageNumber);
|
||||
requests.push(request);
|
||||
}
|
||||
|
||||
return requests;
|
||||
};
|
||||
|
||||
Depaginator.prototype.fetchResources = function(pageNumber) {
|
||||
return $.ajaxJSON(this.url, 'GET', {page: pageNumber});
|
||||
};
|
||||
|
||||
Depaginator.prototype.makeRequests = function(requests) {
|
||||
return $.when.apply($, requests);
|
||||
};
|
||||
|
||||
return depaginate;
|
||||
});
|
|
@ -1,33 +0,0 @@
|
|||
define([
|
||||
'compiled/collections/DateGroupCollection',
|
||||
'underscore'
|
||||
], function(DateGroupCollection, _) {
|
||||
var DueDateCalculator, exists;
|
||||
|
||||
exists = function(value) {
|
||||
return value !== null && value !== undefined;
|
||||
};
|
||||
|
||||
DueDateCalculator = function(assignment) {
|
||||
this.assignment = assignment;
|
||||
};
|
||||
|
||||
DueDateCalculator.prototype.dueDate = function() {
|
||||
var dueAt, allDates, dueDate;
|
||||
|
||||
dueAt = this.assignment.due_at;
|
||||
allDates = this.assignment.all_dates;
|
||||
|
||||
if (!exists(dueAt)) {
|
||||
dueDate = _.find(allDates, section =>
|
||||
exists(section.due_at));
|
||||
if (exists(dueDate)) return dueDate.due_at.toISOString();
|
||||
} else {
|
||||
return dueAt;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
return DueDateCalculator;
|
||||
});
|
|
@ -1,22 +0,0 @@
|
|||
define([
|
||||
'underscore',
|
||||
], function (_) {
|
||||
let EnrollmentsHelper = {
|
||||
studentsThatCanSeeAssignment: function(enrollments, assignment) {
|
||||
let visibleStudentIds = assignment.assignment_visibility;
|
||||
let students = this.students(enrollments);
|
||||
return _.pick(students, visibleStudentIds);
|
||||
},
|
||||
|
||||
students: function(enrollments) {
|
||||
let studentEnrollments = this.studentEnrollments(enrollments);
|
||||
let students = _.pluck(studentEnrollments, 'user');
|
||||
return _.indexBy(students, 'id');
|
||||
},
|
||||
|
||||
studentEnrollments: function(enrollments) {
|
||||
return _.where(enrollments, { type: 'StudentEnrollment' });
|
||||
}
|
||||
};
|
||||
return EnrollmentsHelper;
|
||||
});
|
|
@ -1,16 +0,0 @@
|
|||
define([
|
||||
], function () {
|
||||
function EnrollmentsUrlHelper({ showConcluded = false, showInactive = false }) {
|
||||
if(showConcluded && showInactive) {
|
||||
return 'enrollments_with_concluded_and_inactive_url';
|
||||
} else if(showConcluded) {
|
||||
return 'enrollments_with_concluded_url';
|
||||
} else if(showInactive) {
|
||||
return 'enrollments_with_inactive_url';
|
||||
} else {
|
||||
return 'enrollments_url'
|
||||
}
|
||||
};
|
||||
|
||||
return EnrollmentsUrlHelper;
|
||||
});
|
|
@ -1,185 +0,0 @@
|
|||
define([
|
||||
], function () {
|
||||
function KeyboardNavManager(dimensions) {
|
||||
this.boundaries = {
|
||||
left: 0,
|
||||
right: dimensions.width - 1,
|
||||
top: 0,
|
||||
bottom: dimensions.height - 1
|
||||
};
|
||||
}
|
||||
|
||||
KeyboardNavManager.prototype.makeMove = function(event, currentCellIndex) {
|
||||
var name = this.getKeyboardEventName(event);
|
||||
var newIndex;
|
||||
|
||||
if (currentCellIndex === -1) {
|
||||
newIndex = 0;
|
||||
} else if (name === 'tab' || name === 'rightArrow') {
|
||||
newIndex = this.attemptMoveRight(currentCellIndex);
|
||||
} else if (name === 'shiftTab' || name === 'leftArrow') {
|
||||
newIndex = this.attemptMoveLeft(currentCellIndex);
|
||||
} else if (name === 'enter' || name === 'downArrow') {
|
||||
newIndex = this.attemptMoveDown(currentCellIndex);
|
||||
} else if (name === 'shiftEnter' || name === 'upArrow') {
|
||||
newIndex = this.attemptMoveUp(currentCellIndex);
|
||||
}
|
||||
|
||||
return newIndex;
|
||||
};
|
||||
|
||||
KeyboardNavManager.prototype.getKeyboardEventName = function(event) {
|
||||
var shiftKey = event.shiftKey ? 'shift' : 'noShift';
|
||||
var code = event.keyCode;
|
||||
var eventNames = {
|
||||
9: { noShift: 'tab', shift: 'shiftTab' },
|
||||
13: { noShift: 'enter', shift: 'shiftEnter' },
|
||||
37: { noShift: 'leftArrow' },
|
||||
38: { noShift: 'upArrow' },
|
||||
39: { noShift: 'rightArrow' },
|
||||
40: { noShift: 'downArrow' }
|
||||
};
|
||||
|
||||
return eventNames[code] && eventNames[code][shiftKey];
|
||||
};
|
||||
|
||||
KeyboardNavManager.prototype.restrictedDirections = function(coords) {
|
||||
return {
|
||||
left: coords.x <= this.boundaries.left,
|
||||
right: coords.x >= this.boundaries.right,
|
||||
up: coords.y <= this.boundaries.top,
|
||||
down: coords.y >= this.boundaries.bottom
|
||||
};
|
||||
};
|
||||
|
||||
KeyboardNavManager.prototype.indexToCoords = function(currentCellIndex) {
|
||||
var rowLength = this.boundaries.right + 1;
|
||||
var x = currentCellIndex % rowLength;
|
||||
var y = Math.floor(currentCellIndex / rowLength);
|
||||
this.invalidCellIndexCheck(currentCellIndex);
|
||||
return { x, y };
|
||||
};
|
||||
|
||||
KeyboardNavManager.prototype.coordsToIndex = function(coords) {
|
||||
var rowLength = this.boundaries.right + 1;
|
||||
var yIndexVal = rowLength * coords.y;
|
||||
return coords.x + yIndexVal;
|
||||
};
|
||||
|
||||
KeyboardNavManager.prototype.invalidCellIndexCheck = function(cellIndex) {
|
||||
var maxCoords = { x: this.boundaries.right, y: this.boundaries.bottom };
|
||||
var maxIndex = this.coordsToIndex(maxCoords);
|
||||
var message;
|
||||
if (cellIndex < 0 || cellIndex > maxIndex) {
|
||||
message = 'Invalid cell index of ' + cellIndex + ' provided. The cell ' +
|
||||
'index must be between 0 and ' + maxIndex + ', inclusive.';
|
||||
throw new Error(message);
|
||||
}
|
||||
};
|
||||
|
||||
KeyboardNavManager.prototype.invalidMovementCheck = function(coords, direction) {
|
||||
var message;
|
||||
if (this.restrictedDirections(coords)[direction]) {
|
||||
message = 'Boundary restriction: cannot move ' + direction;
|
||||
throw new Error(message);
|
||||
}
|
||||
};
|
||||
|
||||
KeyboardNavManager.prototype.moveLeft = function(coords) {
|
||||
this.invalidMovementCheck(coords, 'left');
|
||||
return { x: coords.x - 1, y: coords.y };
|
||||
};
|
||||
|
||||
KeyboardNavManager.prototype.moveRight = function(coords) {
|
||||
this.invalidMovementCheck(coords, 'right');
|
||||
return { x: coords.x + 1, y: coords.y };
|
||||
};
|
||||
|
||||
KeyboardNavManager.prototype.moveUp = function(coords) {
|
||||
this.invalidMovementCheck(coords, 'up');
|
||||
return { x: coords.x, y: coords.y - 1 };
|
||||
};
|
||||
|
||||
KeyboardNavManager.prototype.moveDown = function(coords) {
|
||||
this.invalidMovementCheck(coords, 'down');
|
||||
return { x: coords.x, y: coords.y + 1 };
|
||||
};
|
||||
|
||||
KeyboardNavManager.prototype.snapLeft = function(coords) {
|
||||
return { x: this.boundaries.left, y: coords.y };
|
||||
};
|
||||
|
||||
KeyboardNavManager.prototype.snapRight = function(coords) {
|
||||
return { x: this.boundaries.right, y: coords.y };
|
||||
};
|
||||
|
||||
KeyboardNavManager.prototype.snapTop = function(coords) {
|
||||
return { x: coords.x, y: this.boundaries.top };
|
||||
};
|
||||
|
||||
KeyboardNavManager.prototype.snapBottom = function(coords) {
|
||||
return { x: coords.x, y: this.boundaries.bottom };
|
||||
};
|
||||
|
||||
KeyboardNavManager.prototype.attemptMoveLeft = function(currentCellIndex) {
|
||||
var coords = this.indexToCoords(currentCellIndex);
|
||||
var restricted = this.restrictedDirections(coords);
|
||||
|
||||
if (!restricted.left) {
|
||||
coords = this.moveLeft(coords);
|
||||
} else if (restricted.up) {
|
||||
coords = this.snapBottom(this.snapRight(coords));
|
||||
} else {
|
||||
coords = this.moveUp(this.snapRight(coords));
|
||||
}
|
||||
|
||||
return this.coordsToIndex(coords);
|
||||
};
|
||||
|
||||
KeyboardNavManager.prototype.attemptMoveRight = function(currentCellIndex) {
|
||||
var coords = this.indexToCoords(currentCellIndex);
|
||||
var restricted = this.restrictedDirections(coords);
|
||||
|
||||
if (!restricted.right) {
|
||||
coords = this.moveRight(coords);
|
||||
} else if (restricted.down) {
|
||||
coords = this.snapTop(this.snapLeft(coords));
|
||||
} else {
|
||||
coords = this.moveDown(this.snapLeft(coords));
|
||||
}
|
||||
|
||||
return this.coordsToIndex(coords);
|
||||
};
|
||||
|
||||
KeyboardNavManager.prototype.attemptMoveUp = function(currentCellIndex) {
|
||||
var coords = this.indexToCoords(currentCellIndex);
|
||||
var restricted = this.restrictedDirections(coords);
|
||||
|
||||
if (!restricted.up) {
|
||||
coords = this.moveUp(coords);
|
||||
} else if (restricted.left) {
|
||||
coords = this.snapRight(this.snapBottom(coords));
|
||||
} else {
|
||||
coords = this.moveLeft(this.snapBottom(coords));
|
||||
}
|
||||
|
||||
return this.coordsToIndex(coords);
|
||||
};
|
||||
|
||||
KeyboardNavManager.prototype.attemptMoveDown = function(currentCellIndex) {
|
||||
var coords = this.indexToCoords(currentCellIndex);
|
||||
var restricted = this.restrictedDirections(coords);
|
||||
|
||||
if (!restricted.down) {
|
||||
coords = this.moveDown(coords);
|
||||
} else if (restricted.right) {
|
||||
coords = this.snapLeft(this.snapTop(coords));
|
||||
} else {
|
||||
coords = this.moveRight(this.snapTop(coords));
|
||||
}
|
||||
|
||||
return this.coordsToIndex(coords);
|
||||
};
|
||||
|
||||
return KeyboardNavManager;
|
||||
});
|
|
@ -1,22 +0,0 @@
|
|||
define([
|
||||
'underscore',
|
||||
], function (_) {
|
||||
let SubmissionsHelper = {
|
||||
submissionsForAssignment: function(submissionGroups, assignment) {
|
||||
let submissions = this.extractSubmissions(submissionGroups);
|
||||
let subsForAssignment = submissions[assignment.id]
|
||||
return subsForAssignment ? _.indexBy(subsForAssignment, 'user_id') : {};
|
||||
},
|
||||
|
||||
extractSubmissions: function(submissionGroups) {
|
||||
return _.chain(submissionGroups)
|
||||
.values()
|
||||
.flatten()
|
||||
.pluck('submissions')
|
||||
.flatten()
|
||||
.groupBy('assignment_id')
|
||||
.value();
|
||||
}
|
||||
};
|
||||
return SubmissionsHelper;
|
||||
});
|
|
@ -1,38 +0,0 @@
|
|||
define([
|
||||
'underscore',
|
||||
'../actions/submissionsActions'
|
||||
], function (_, SubmissionsActions) {
|
||||
var GradeCellMixin = {
|
||||
getInitialState() {
|
||||
return {
|
||||
submission: this.props.submission,
|
||||
};
|
||||
},
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.setState({submission: nextProps.submission});
|
||||
},
|
||||
|
||||
getDisplayGrade() {
|
||||
var submission = this.props.cellData;
|
||||
return (submission && submission.grade) ? submission.grade : '-';
|
||||
},
|
||||
|
||||
isSubmissionGradedAsNull() {
|
||||
return this.props.cellData && _.isNull(this.props.cellData.grade);
|
||||
},
|
||||
|
||||
sendSubmission() {
|
||||
var submission = {
|
||||
userId: this.props.rowData.student.user_id,
|
||||
assignmentId: this.props.columnData.assignment.id,
|
||||
postedGrade: this.state.gradeToPost
|
||||
};
|
||||
|
||||
SubmissionsActions.updateGrade(submission);
|
||||
this.setState({gradeToPost: null});
|
||||
},
|
||||
};
|
||||
|
||||
return GradeCellMixin;
|
||||
});
|
|
@ -1,22 +0,0 @@
|
|||
define([], function () {
|
||||
var StandardCellFocusMixin = {
|
||||
componentDidUpdate(previousProps, previousState) {
|
||||
var isActiveCell = this.props.isActiveCell,
|
||||
gradeHasChanged = (this.state.gradeToPost != previousState.grade);
|
||||
|
||||
if (previousProps.isActiveCell && !isActiveCell) {
|
||||
var gradeToPost = this.state.gradeToPost;
|
||||
if (gradeToPost && this.getDisplayGrade() !== gradeToPost) {
|
||||
this.sendSubmission();
|
||||
}
|
||||
}
|
||||
|
||||
if(isActiveCell && !gradeHasChanged && !this.isConcluded()) {
|
||||
var gradeInput = this.refs.gradeInput;
|
||||
gradeInput.select();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return StandardCellFocusMixin;
|
||||
});
|
|
@ -1,20 +0,0 @@
|
|||
define(['react'], function (React) {
|
||||
var StandardGradeInputMixin = {
|
||||
handleOnChange(event) {
|
||||
this.setState({gradeToPost: event.target.value});
|
||||
},
|
||||
|
||||
renderEditGrade() {
|
||||
return (
|
||||
<input
|
||||
onChange={this.handleOnChange}
|
||||
className="grade"
|
||||
ref="gradeInput"
|
||||
type="text"
|
||||
value={this.state.gradeToPost || this.getDisplayGrade()}/>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
return StandardGradeInputMixin;
|
||||
});
|
|
@ -1,13 +0,0 @@
|
|||
define([], function () {
|
||||
var StandardRenderMixin = {
|
||||
isConcluded() {
|
||||
return this.props.rowData.isConcluded;
|
||||
},
|
||||
|
||||
render() {
|
||||
return (this.props.isActiveCell && !this.isConcluded()) ? this.renderEditGrade() : this.renderViewGrade();
|
||||
}
|
||||
};
|
||||
|
||||
return StandardRenderMixin;
|
||||
});
|
|
@ -1,109 +0,0 @@
|
|||
define([
|
||||
'reflux',
|
||||
'underscore',
|
||||
'../actions/assignmentGroupsActions',
|
||||
'jsx/shared/helpers/dateHelper',
|
||||
'../constants'
|
||||
], function (Reflux, _, AssignmentGroupsActions, DateHelper, GradebookConstants) {
|
||||
var AssignmentGroupsStore = Reflux.createStore({
|
||||
listenables: [AssignmentGroupsActions],
|
||||
|
||||
init() {
|
||||
this.state = {
|
||||
data: null,
|
||||
error: null
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
if (this.state === undefined) {
|
||||
this.init();
|
||||
}
|
||||
return this.state;
|
||||
},
|
||||
|
||||
onLoadFailed(error) {
|
||||
this.state.error = error;
|
||||
this.trigger(this.state);
|
||||
},
|
||||
|
||||
onLoadCompleted(json) {
|
||||
var assignmentGroups;
|
||||
this.setNoPointsWarning(json);
|
||||
assignmentGroups = this.formatAssignmentGroups(json);
|
||||
this.state.data = assignmentGroups;
|
||||
this.trigger(this.state);
|
||||
},
|
||||
|
||||
onReplaceAssignmentGroups(updatedAssignmentGroups) {
|
||||
this.state.data = updatedAssignmentGroups;
|
||||
this.trigger(this.state);
|
||||
},
|
||||
|
||||
setNoPointsWarning(assignmentGroups) {
|
||||
_.each(assignmentGroups, (group) => {
|
||||
var pointsPossible = _.inject(group.assignments, (sum, assignment) => {
|
||||
return sum + (assignment.points_possible || 0);
|
||||
}, 0);
|
||||
|
||||
group.shouldShowNoPointsWarning = (pointsPossible === 0);
|
||||
});
|
||||
},
|
||||
|
||||
onReplaceAssignment(updatedAssignment) {
|
||||
var assignments = _.flatten(_.pluck(this.state.data, 'assignments')),
|
||||
assignment = _.find(assignments, assignment => updatedAssignment.id === assignment.id);
|
||||
|
||||
assignment.muted = updatedAssignment.muted;
|
||||
this.trigger(this.state);
|
||||
},
|
||||
|
||||
formatAssignmentGroups(groups) {
|
||||
return _.map(groups, (group) => {
|
||||
group.assignments = _.chain(group.assignments)
|
||||
.reject(assignment => _.contains(assignment.submission_types, 'not_graded'))
|
||||
.map(assignment => this.formatAssignment(assignment, group))
|
||||
.value();
|
||||
|
||||
return group;
|
||||
});
|
||||
},
|
||||
|
||||
formatAssignment(assignment, assignmentGroup) {
|
||||
assignment = DateHelper.parseDates(assignment, GradebookConstants.ASSIGNMENT_DATES);
|
||||
assignment.assignment_group_position = assignmentGroup.position;
|
||||
assignment.speedgrader_url = GradebookConstants.context_url + '/gradebook/speed_grader?assignment_id=' + assignment.id;
|
||||
assignment.submissions_downloads = 0;
|
||||
assignment.shouldShowNoPointsWarning = assignmentGroup.shouldShowNoPointsWarning;
|
||||
|
||||
if (assignment.has_overrides) {
|
||||
assignment.overrides = _.map(
|
||||
assignment.overrides,
|
||||
override => DateHelper.parseDates(override, GradebookConstants.OVERRIDE_DATES)
|
||||
);
|
||||
}
|
||||
return assignment;
|
||||
},
|
||||
|
||||
/*
|
||||
["id"] -> [Assignment]
|
||||
Given a list of assignment ids, retrieves the specified assignments
|
||||
*/
|
||||
assignments(assignmentIds) {
|
||||
var assignmentGroups, assignments, allAssignments;
|
||||
|
||||
assignmentGroups = this.state.data;
|
||||
allAssignments = _.map(assignmentGroups, group => group.assignments);
|
||||
allAssignments = _.flatten(allAssignments);
|
||||
|
||||
assignments = _.map(assignmentIds, assignmentId =>
|
||||
_.find(allAssignments, assignment =>
|
||||
assignment.id === assignmentId));
|
||||
|
||||
assignments = _.reject(assignments, assignment => assignment === undefined);
|
||||
return assignments;
|
||||
}
|
||||
});
|
||||
|
||||
return AssignmentGroupsStore;
|
||||
});
|
|
@ -1,78 +0,0 @@
|
|||
define([
|
||||
'reflux',
|
||||
'underscore',
|
||||
'jsx/gradebook/grid/actions/customColumnsActions'
|
||||
], function (Reflux, _, CustomColumnsActions) {
|
||||
var CustomColumnsStore = Reflux.createStore({
|
||||
listenables: [CustomColumnsActions],
|
||||
|
||||
init() {
|
||||
this.state = {
|
||||
teacherNotes: null,
|
||||
customColumns: {
|
||||
data: [],
|
||||
columnData: {}
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
if(this.state === undefined) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
return this.state;
|
||||
},
|
||||
|
||||
customColumns(data) {
|
||||
return _.isUndefined(data) ? [] : _.reject(data, column => column.hidden || column.teacher_notes);
|
||||
},
|
||||
|
||||
onLoadCompleted(data) {
|
||||
this.state.customColumns.data = this.customColumns(data);
|
||||
this.trigger(this.state);
|
||||
},
|
||||
|
||||
onLoadTeacherNotesCompleted(teacherNotes) {
|
||||
var notes = _.isUndefined(teacherNotes) ? [] : teacherNotes;
|
||||
this.state.teacherNotes = notes;
|
||||
this.trigger(this.state);
|
||||
},
|
||||
|
||||
onLoadColumnDataCompleted(data, columnId) {
|
||||
if (this.state.customColumns.columnData[columnId] === undefined) {
|
||||
this.state.customColumns.columnData[columnId] = {};
|
||||
}
|
||||
|
||||
_.each(data, function(columnDatum) {
|
||||
this.state.customColumns.columnData[columnId][columnDatum.user_id] = columnDatum;
|
||||
}.bind(this));
|
||||
|
||||
},
|
||||
|
||||
getColumnDatum(columnId, userId) {
|
||||
var columnData, columnDatum;
|
||||
|
||||
columnData = this.state.customColumns.columnData[columnId];
|
||||
if (columnData !== undefined) {
|
||||
columnDatum = columnData[userId];
|
||||
return columnDatum;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
},
|
||||
|
||||
onUpdateTeacherNote(noteData) {
|
||||
var teacherNotes = this.state.teacherNotes,
|
||||
existingNote = _.find(teacherNotes, note => note.user_id === noteData.user_id);
|
||||
if (existingNote) {
|
||||
existingNote.content = noteData.content;
|
||||
} else {
|
||||
teacherNotes.push(noteData);
|
||||
}
|
||||
this.trigger(this.state);
|
||||
}
|
||||
});
|
||||
|
||||
return CustomColumnsStore;
|
||||
});
|
|
@ -1,77 +0,0 @@
|
|||
|
||||
define([
|
||||
'reflux',
|
||||
'../actions/gradebookToolbarActions',
|
||||
'jquery',
|
||||
'underscore',
|
||||
'i18n!gradebook2',
|
||||
'compiled/userSettings',
|
||||
'../constants'
|
||||
], function (Reflux, GradebookToolbarActions, $, _, I18n, userSettings, GradebookConstants) {
|
||||
var GradebookToolbarStore = Reflux.createStore({
|
||||
listenables: [GradebookToolbarActions],
|
||||
|
||||
init() {
|
||||
var storedSortOrder = GradebookConstants.gradebook_column_order_settings ||
|
||||
{ sortType: 'assignment_group' };
|
||||
|
||||
var savedOptions = {
|
||||
hideStudentNames: userSettings.contextGet('hideStudentNames'),
|
||||
hideNotesColumn: !GradebookConstants.teacher_notes || GradebookConstants.teacher_notes.hidden,
|
||||
arrangeColumnsBy: storedSortOrder.sortType,
|
||||
treatUngradedAsZero: userSettings.contextGet('treatUngradedAsZero'),
|
||||
totalColumnInFront: userSettings.contextGet('total_column_in_front'),
|
||||
warnedAboutTotalsDisplay: userSettings.contextGet('warned_about_totals_display'),
|
||||
showTotalGradeAsPoints: GradebookConstants.show_total_grade_as_points
|
||||
};
|
||||
|
||||
this.toolbarOptions = _.defaults(savedOptions, GradebookConstants.DEFAULT_TOOLBAR_PREFERENCES);
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
if (this.toolbarOptions === null || this.toolbarOptions === undefined) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
return this.toolbarOptions;
|
||||
},
|
||||
|
||||
onToggleStudentNames(hideStudentNames) {
|
||||
this.toolbarOptions.hideStudentNames = hideStudentNames;
|
||||
this.trigger(this.toolbarOptions);
|
||||
},
|
||||
|
||||
onToggleNotesColumn(hideNotesColumn) {
|
||||
this.toolbarOptions.hideNotesColumn = hideNotesColumn;
|
||||
this.trigger(this.toolbarOptions);
|
||||
},
|
||||
|
||||
onArrangeColumnsBy(criteria) {
|
||||
this.toolbarOptions.arrangeColumnsBy = criteria;
|
||||
this.trigger(this.toolbarOptions);
|
||||
},
|
||||
|
||||
onToggleTreatUngradedAsZero(treatUngradedAsZero) {
|
||||
this.toolbarOptions.treatUngradedAsZero = treatUngradedAsZero;
|
||||
this.trigger(this.toolbarOptions);
|
||||
},
|
||||
|
||||
onToggleTotalColumnInFront(totalColumnInFront) {
|
||||
this.toolbarOptions.totalColumnInFront = totalColumnInFront;
|
||||
this.trigger(this.toolbarOptions);
|
||||
},
|
||||
|
||||
onShowTotalGradeAsPoints(showAsPoints) {
|
||||
this.toolbarOptions.showTotalGradeAsPoints = showAsPoints;
|
||||
this.trigger(this.toolbarOptions);
|
||||
},
|
||||
|
||||
onHideTotalDisplayWarning(hideWarning) {
|
||||
this.toolbarOptions.warnedAboutTotalsDisplay = hideWarning;
|
||||
this.trigger(this.toolbarOptions);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return GradebookToolbarStore;
|
||||
});
|
|
@ -1,149 +0,0 @@
|
|||
define([
|
||||
'reflux',
|
||||
'underscore',
|
||||
'jsx/gradebook/grid/actions/gradingPeriodsActions',
|
||||
'jsx/gradebook/grid/stores/gradingPeriodsStore',
|
||||
'jsx/gradebook/grid/helpers/dueDateCalculator',
|
||||
'jsx/gradebook/grid/constants',
|
||||
'compiled/userSettings'
|
||||
], function (Reflux, _, GradingPeriodsActions, GradingPeriodsStore,
|
||||
DueDateCalculator, GradebookConstants, userSettings) {
|
||||
var GradePeriodsStore = Reflux.createStore({
|
||||
|
||||
listenables: [GradingPeriodsActions],
|
||||
|
||||
selected() {
|
||||
return _.find(this.gradingPeriods.data, function(period) {
|
||||
return period.id === this.gradingPeriods.selected;
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
init() {
|
||||
var allPeriodsOption, activeGradingPeriods;
|
||||
|
||||
allPeriodsOption = {
|
||||
end_date: new Date().setYear(3000),
|
||||
id: '0',
|
||||
is_last: false,
|
||||
start_date: new Date().setYear(0),
|
||||
title: 'All Grading Periods'
|
||||
};
|
||||
|
||||
activeGradingPeriods = GradebookConstants.active_grading_periods;
|
||||
|
||||
this.gradingPeriods = {
|
||||
data: [allPeriodsOption].concat(activeGradingPeriods),
|
||||
selected: null,
|
||||
error: null
|
||||
};
|
||||
|
||||
this.gradingPeriods.selected = this.gradePeriodOnLoad();
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
if (this.gradingPeriods === null || this.gradingPeriods === undefined) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
return this.gradingPeriods;
|
||||
},
|
||||
|
||||
// handler for selecting a grading period
|
||||
onSelect(periodData) {
|
||||
var selectedPeriod, allPeriods, periodMatcher;
|
||||
|
||||
allPeriods = this.gradingPeriods.data;
|
||||
periodMatcher = period => period.id === periodData.id;
|
||||
selectedPeriod = _.find(allPeriods, periodMatcher);
|
||||
|
||||
if (selectedPeriod !== null && selectedPeriod !== undefined) {
|
||||
this.gradingPeriods.selected = selectedPeriod.id;
|
||||
this.trigger(this.gradingPeriods);
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
([Assignment], GradingPeriod) -> [Assignment]
|
||||
Given a list of assignments and a grading period, returns the assignments
|
||||
which are in the grading period
|
||||
*/
|
||||
assignmentsInPeriod(assignments, period) {
|
||||
var assignmentList;
|
||||
|
||||
assignmentList = _.filter(assignments, assignment =>
|
||||
this.assignmentIsInPeriod(assignment, period));
|
||||
|
||||
return assignmentList;
|
||||
},
|
||||
|
||||
/*
|
||||
(Assignment, GradingPeriod) -> Boolean
|
||||
Given an assignment and a grading period, checks if assignment is in the
|
||||
given grading period
|
||||
*/
|
||||
assignmentIsInPeriod(assignment, period) {
|
||||
var dueDateString, assignmentDueDate, periodStartDate,
|
||||
periodEndDate, result;
|
||||
|
||||
dueDateString = new DueDateCalculator(assignment).dueDate();
|
||||
assignmentDueDate = new Date(dueDateString);
|
||||
periodStartDate = new Date(period.start_date);
|
||||
periodEndDate = new Date(period.end_date);
|
||||
|
||||
result = (assignmentDueDate >= periodStartDate && assignmentDueDate <= periodEndDate)
|
||||
|| ((assignmentDueDate === null || dueDateString === null) && period === this.lastPeriod())
|
||||
|| period.id === '0';
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
/*
|
||||
"Integer" -> Boolean
|
||||
Given the id of a grading period (string format), checks whether the
|
||||
grading period is active.
|
||||
*/
|
||||
periodIsActive(periodId) {
|
||||
var result;
|
||||
|
||||
result = _.chain(this.gradingPeriods.data)
|
||||
.map(period => period.id)
|
||||
.contains(periodId)
|
||||
.value();
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
/*
|
||||
() -> GradingPeriod
|
||||
Returns the last (active) grading period of the course
|
||||
*/
|
||||
lastPeriod() {
|
||||
var last;
|
||||
|
||||
last = _.find(this.gradingPeriods.data, gradingPeriod => gradingPeriod.is_last);
|
||||
|
||||
return last;
|
||||
},
|
||||
|
||||
/*
|
||||
() -> "Integer"
|
||||
Determines which grading period should be loaded when gradebook is opened
|
||||
*/
|
||||
gradePeriodOnLoad() {
|
||||
var currentPeriodId = userSettings.contextGet('gradebook_current_grading_period');
|
||||
|
||||
if (!(currentPeriodId &&
|
||||
(currentPeriodId === '0' || this.periodIsActive(currentPeriodId)))) {
|
||||
currentPeriodId = GradebookConstants.current_grading_period_id;
|
||||
}
|
||||
|
||||
if (currentPeriodId === null || currentPeriodId === undefined) {
|
||||
currentPeriodId = '0';
|
||||
}
|
||||
|
||||
return currentPeriodId;
|
||||
}
|
||||
});
|
||||
|
||||
return GradePeriodsStore;
|
||||
});
|
|
@ -1,62 +0,0 @@
|
|||
define([
|
||||
'reflux',
|
||||
'underscore',
|
||||
'jquery',
|
||||
'jsx/gradebook/grid/actions/keyboardNavigationActions',
|
||||
'jsx/gradebook/grid/helpers/keyboardNavManager'
|
||||
], function (Reflux, _, $, KeyboardNavigationActions, KeyboardNavigationManager) {
|
||||
|
||||
var KeyboardNavigationStore = Reflux.createStore({
|
||||
listenables: [KeyboardNavigationActions],
|
||||
|
||||
init() {
|
||||
this.state = {
|
||||
currentCellIndex: -1,
|
||||
currentColumnIndex: -1,
|
||||
currentRowIndex: -1
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
if(this.state === undefined) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
return this.state;
|
||||
},
|
||||
|
||||
rowCount(columnCount) {
|
||||
var cellCount;
|
||||
if (columnCount === 0) return 0;
|
||||
cellCount = $('.gradebook-cell').size();
|
||||
return cellCount / columnCount;
|
||||
},
|
||||
|
||||
columnCount() {
|
||||
return $('.fixedDataTableCellLayout_columnResizerContainer').size();
|
||||
},
|
||||
|
||||
onConstructKeyboardNavManager() {
|
||||
var columnCount = this.columnCount();
|
||||
var rowCount = this.rowCount(columnCount);
|
||||
var dimensions = { width: columnCount, height: rowCount };
|
||||
this.navManager = new KeyboardNavigationManager(dimensions);
|
||||
},
|
||||
|
||||
onHandleKeyboardEvent(event) {
|
||||
var newIndex = this.navManager.makeMove(event, this.state.currentCellIndex);
|
||||
if (_.isNumber(newIndex)) this.onSetActiveCell(newIndex);
|
||||
},
|
||||
|
||||
onSetActiveCell(cellIndex) {
|
||||
var coords = this.navManager.indexToCoords(cellIndex);
|
||||
|
||||
this.state.currentCellIndex = cellIndex;
|
||||
this.state.currentColumnIndex = coords.x;
|
||||
this.state.currentRowIndex = coords.y;
|
||||
this.trigger(this.state);
|
||||
}
|
||||
});
|
||||
|
||||
return KeyboardNavigationStore;
|
||||
});
|
|
@ -1,68 +0,0 @@
|
|||
define([
|
||||
'reflux',
|
||||
'underscore',
|
||||
'jsx/gradebook/grid/actions/sectionsActions',
|
||||
'compiled/userSettings'
|
||||
], function(Reflux, _, SectionsActions, userSettings) {
|
||||
var SectionsStore = Reflux.createStore({
|
||||
listenables: [SectionsActions],
|
||||
|
||||
init() {
|
||||
this.state = {
|
||||
sections: null,
|
||||
error: null,
|
||||
selected: this.sectionOnLoad()
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
if (this.state === undefined) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
return this.state;
|
||||
},
|
||||
|
||||
onLoadCompleted(sectionData) {
|
||||
var allSectionsOption;
|
||||
|
||||
allSectionsOption = {
|
||||
id: '0',
|
||||
name: 'All Sections'
|
||||
};
|
||||
|
||||
sectionData.unshift(allSectionsOption);
|
||||
|
||||
this.state.sections = sectionData;
|
||||
this.trigger(this.state);
|
||||
},
|
||||
|
||||
onLoadFailed(error) {
|
||||
this.state.error = error;
|
||||
this.trigger(this.state);
|
||||
},
|
||||
|
||||
onSelectSection(sectionId) {
|
||||
this.state.selected = sectionId;
|
||||
this.trigger(this.state);
|
||||
},
|
||||
|
||||
sectionOnLoad() {
|
||||
var defaultSectionId = '0';
|
||||
return userSettings.contextGet('grading_show_only_section') || defaultSectionId;
|
||||
},
|
||||
|
||||
selected() {
|
||||
var selectedId, currentSection, sections;
|
||||
|
||||
selectedId = this.state.selected;
|
||||
sections = this.state.sections;
|
||||
|
||||
currentSection = _.find(sections, section => section.id === selectedId);
|
||||
|
||||
return currentSection;
|
||||
},
|
||||
});
|
||||
|
||||
return SectionsStore;
|
||||
});
|
|
@ -1,52 +0,0 @@
|
|||
define([
|
||||
'reflux',
|
||||
'jquery',
|
||||
'../actions/settingsActions'
|
||||
], function (Reflux, $, SettingsActions) {
|
||||
var MOUNT_ELEMENT = document.getElementById('gradebook-grid-wrapper'),
|
||||
PADDING = 20,
|
||||
TOOLBAR_HEIGHT = $('#gradebook-toolbar').height(),
|
||||
TOOLBAR_OFFSET = $('#gradebook-toolbar').offset().top;
|
||||
|
||||
var SettingsStore = Reflux.createStore({
|
||||
listenables: [SettingsActions],
|
||||
|
||||
init () {
|
||||
this.columnWidths = ENV.GRADEBOOK_OPTIONS.gradebook_column_size_settings || {};
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
if(this.settings === undefined) {
|
||||
this.settings = {
|
||||
width: this.getGradebookWidth(),
|
||||
height: this.getGradebookHeight(),
|
||||
columnWidths: this.columnWidths
|
||||
};
|
||||
}
|
||||
|
||||
return this.settings;
|
||||
},
|
||||
|
||||
onResize() {
|
||||
this.settings.width = this.getGradebookWidth();
|
||||
this.settings.height = this.getGradebookHeight();
|
||||
this.trigger(this.settings);
|
||||
},
|
||||
|
||||
onSaveColumnSize(newColumnWidth, dataKey) {
|
||||
this.columnWidths[dataKey] = newColumnWidth;
|
||||
this.trigger(this.settings);
|
||||
},
|
||||
|
||||
getGradebookWidth() {
|
||||
return $(MOUNT_ELEMENT).width();
|
||||
},
|
||||
|
||||
getGradebookHeight() {
|
||||
var windowHeight = $(window).innerHeight();
|
||||
return windowHeight - (TOOLBAR_HEIGHT + TOOLBAR_OFFSET + PADDING);
|
||||
}
|
||||
});
|
||||
|
||||
return SettingsStore;
|
||||
});
|
|
@ -1,95 +0,0 @@
|
|||
define([
|
||||
'reflux',
|
||||
'underscore',
|
||||
'jsx/gradebook/grid/actions/studentEnrollmentsActions',
|
||||
'jsx/gradebook/grid/stores/sectionsStore'
|
||||
], function (Reflux, _, StudentEnrollmentsActions, SectionsStore) {
|
||||
var StudentEnrollmentsStore = Reflux.createStore({
|
||||
listenables: [
|
||||
StudentEnrollmentsActions
|
||||
],
|
||||
|
||||
init() {
|
||||
this.listenTo(SectionsStore, this.onSectionSelected);
|
||||
this.state = {
|
||||
data: null,
|
||||
error: null,
|
||||
all: null
|
||||
}
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
if (this.state === undefined) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
return this.state;
|
||||
},
|
||||
|
||||
onLoadFailed(error) {
|
||||
this.state.error = error;
|
||||
this.trigger(this.state);
|
||||
},
|
||||
|
||||
onLoadCompleted(studentEnrollmentData) {
|
||||
this.state.all = studentEnrollmentData;
|
||||
this.state.inCurrentSection = this.studentsInSection(SectionsStore.selected());
|
||||
this.state.data = this.state.inCurrentSection;
|
||||
|
||||
this.trigger(this.state);
|
||||
},
|
||||
|
||||
onSearch(searchTerm) {
|
||||
this.applySearch(searchTerm);
|
||||
this.trigger(this.state);
|
||||
},
|
||||
|
||||
onSectionSelected() {
|
||||
var selectedSection, studentsInSection;
|
||||
|
||||
selectedSection = SectionsStore.selected();
|
||||
studentsInSection = this.studentsInSection(selectedSection);
|
||||
|
||||
if (studentsInSection === null || studentsInSection === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.state.inCurrentSection = studentsInSection;
|
||||
this.state.data = studentsInSection;
|
||||
this.applySearch(this.state.searchTerm);
|
||||
this.trigger(this.state);
|
||||
},
|
||||
|
||||
applySearch(searchTerm) {
|
||||
if (searchTerm !== null && searchTerm !== undefined) {
|
||||
var pattern = new RegExp(searchTerm.toLowerCase()), predicate = function(enrollment) {
|
||||
var user = enrollment.user;
|
||||
return (user.name && user.name.toLowerCase().match(pattern)) ||
|
||||
(user.sis_login_id && user.sis_login_id.toLowerCase().match(pattern)) ||
|
||||
(user.login_id && user.login_id.toLowerCase().match(pattern));
|
||||
};
|
||||
|
||||
this.state.searchTerm = searchTerm;
|
||||
this.state.data = _.filter(this.state.inCurrentSection, predicate);
|
||||
}
|
||||
},
|
||||
|
||||
studentsInSection(selectedSection) {
|
||||
var students, filteredStudents;
|
||||
|
||||
students = this.state.all;
|
||||
|
||||
if (_.isUndefined(students)) {
|
||||
return undefined;
|
||||
} else if (_.isUndefined(selectedSection) || selectedSection.id === '0'){
|
||||
return students;
|
||||
}
|
||||
|
||||
filteredStudents = _.filter(students, student => student.course_section_id === selectedSection.id);
|
||||
|
||||
return filteredStudents;
|
||||
}
|
||||
});
|
||||
|
||||
return StudentEnrollmentsStore;
|
||||
});
|
|
@ -1,160 +0,0 @@
|
|||
define([
|
||||
'reflux',
|
||||
'underscore',
|
||||
'jquery',
|
||||
'../actions/submissionsActions',
|
||||
'../stores/assignmentGroupsStore',
|
||||
'../stores/gradingPeriodsStore',
|
||||
'compiled/gradebook2/GradebookTranslations',
|
||||
'compiled/jquery.rails_flash_notifications'
|
||||
], function (Reflux, _, $, SubmissionsActions, AssignmentGroupsStore,
|
||||
GradingPeriodsStore, GRADEBOOK_TRANSLATIONS) {
|
||||
|
||||
var SubmissionsStore = Reflux.createStore({
|
||||
listenables: [SubmissionsActions],
|
||||
|
||||
init() {
|
||||
this.state = {
|
||||
data: null,
|
||||
error: null,
|
||||
selected: null
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
if (this.state === null || this.state === undefined) {
|
||||
this.init();
|
||||
}
|
||||
return this.state;
|
||||
},
|
||||
|
||||
onUpdateGradeCompleted(postedGrade, response) {
|
||||
var userSubmissions =
|
||||
_.find(this.state.data, (s) => s.user_id === postedGrade.userId)
|
||||
.submissions;
|
||||
|
||||
var submission = _.find(userSubmissions, (s) => s.id === response.id);
|
||||
|
||||
if (submission) {
|
||||
var submissionIndex = _.indexOf(_.pluck(userSubmissions, 'id'), response.id);
|
||||
userSubmissions[submissionIndex] = response;
|
||||
} else {
|
||||
userSubmissions.push(response);
|
||||
}
|
||||
|
||||
this.trigger(this.state);
|
||||
},
|
||||
|
||||
onUpdateGradeFailed() {
|
||||
$.flashError(GRADEBOOK_TRANSLATIONS.submission_update_error);
|
||||
},
|
||||
|
||||
onLoadFailed(error) {
|
||||
this.state.error = error;
|
||||
this.trigger(this.state);
|
||||
},
|
||||
|
||||
onLoadCompleted(submissions) {
|
||||
this.state.data = submissions;
|
||||
this.trigger(this.state);
|
||||
},
|
||||
|
||||
updateSubmissions(currentSubs, updatedSubs) {
|
||||
var mergedSubmissions = _.extend(
|
||||
_.indexBy(currentSubs, 'id'),
|
||||
_.indexBy(updatedSubs, 'id')
|
||||
);
|
||||
return _.values(mergedSubmissions);
|
||||
},
|
||||
|
||||
onUpdatedSubmissionsReceived(updatedSubs) {
|
||||
var updatedSubsForGroup;
|
||||
this.state.data = _.map(this.state.data, (submissionGroup) => {
|
||||
updatedSubsForGroup = _.filter(
|
||||
updatedSubs,
|
||||
updatedSub => updatedSub.user_id === submissionGroup.user_id
|
||||
);
|
||||
submissionGroup.submissions = this.updateSubmissions(submissionGroup.submissions, updatedSubsForGroup);
|
||||
return submissionGroup;
|
||||
});
|
||||
|
||||
this.trigger(this.state);
|
||||
},
|
||||
|
||||
/*
|
||||
([Submission], [Assignment]) -> [Submission]
|
||||
Given a list of submissions and a list of assignments, filters out the
|
||||
submissions which don't belong to an assignment in the list of
|
||||
assignments
|
||||
*/
|
||||
filterSubmissions(submissions, assignments) {
|
||||
var assignmentIds, filteredSubmissions;
|
||||
|
||||
assignmentIds = _.map(assignments, assignment => assignment.id);
|
||||
filteredSubmissions = _.filter(submissions, submission =>
|
||||
_.contains(assignmentIds, submission.assignment_id));
|
||||
|
||||
return filteredSubmissions;
|
||||
},
|
||||
|
||||
/*
|
||||
([Submission], [AssignmentGroup]) -> [AssignmentGroup]
|
||||
Given a list of submissions and assignment groups, Returns the assignment
|
||||
groups which have a submission in the list.
|
||||
*/
|
||||
assignmentGroupsForSubmissions(submissions, assignmentGroups) {
|
||||
var assignmentIds, relevantGroups;
|
||||
|
||||
assignmentIds = _.map(submissions, s => s.assignment_id);
|
||||
relevantGroups = _.filter(assignmentGroups, assignmentGroup =>
|
||||
_.filter(assignmentGroup.assignments, a =>
|
||||
_.contains(assignmentIds, a.id)).length > 0
|
||||
);
|
||||
|
||||
return relevantGroups;
|
||||
},
|
||||
|
||||
/*
|
||||
[Submission] -> [Assignment]
|
||||
Given a list of submissions, returns a list of assignments which those
|
||||
submissions belong to. Assignments are not guaranteed to be unique. If
|
||||
uniqueness is required, use `_.uniq` on the result.
|
||||
*/
|
||||
assignmentsForSubmissions(submissions) {
|
||||
var assignmentIds, allAssignments;
|
||||
|
||||
assignmentIds = _.map(submissions, submission => submission.assignment_id);
|
||||
allAssignments = AssignmentGroupsStore.assignments(assignmentIds);
|
||||
|
||||
return allAssignments;
|
||||
},
|
||||
|
||||
/*
|
||||
([Submission], GradingPeriod) -> [Submission]
|
||||
Takes a list of submissions and returns the submissions in that list
|
||||
which are in the given grading period
|
||||
*/
|
||||
submissionsInPeriod(submissions, period) {
|
||||
var periodAssignments, periodSubmissions, assignments;
|
||||
|
||||
assignments = this.assignmentsForSubmissions(submissions);
|
||||
periodAssignments = GradingPeriodsStore.assignmentsInPeriod(assignments, period);
|
||||
periodSubmissions = this.filterSubmissions(submissions, periodAssignments);
|
||||
|
||||
return periodSubmissions;
|
||||
},
|
||||
|
||||
/*
|
||||
[Submission] -> [Submission]
|
||||
Takes a list of submissions and returns the submissions in that list
|
||||
which are in the current grading period
|
||||
*/
|
||||
submissionsInCurrentPeriod(submissions) {
|
||||
var currentPeriod = GradingPeriodsStore.selected();
|
||||
|
||||
return this.submissionsInPeriod(submissions, currentPeriod);
|
||||
}
|
||||
});
|
||||
|
||||
return SubmissionsStore;
|
||||
});
|
|
@ -1,165 +0,0 @@
|
|||
define([
|
||||
'reflux',
|
||||
'underscore',
|
||||
'jsx/gradebook/grid/constants',
|
||||
'jsx/gradebook/grid/stores/studentEnrollmentsStore',
|
||||
'jsx/gradebook/grid/stores/gradebookToolbarStore',
|
||||
'jsx/gradebook/grid/stores/assignmentGroupsStore',
|
||||
'jsx/gradebook/grid/stores/gradingPeriodsStore',
|
||||
'jsx/gradebook/grid/stores/submissionsStore',
|
||||
'jsx/gradebook/grid/stores/customColumnsStore',
|
||||
'jsx/gradebook/grid/actions/tableActions'
|
||||
], function (Reflux, _, GradebookConstants, StudentEnrollmentsStore, GradebookToolbarStore,
|
||||
AssignmentGroupsStore, GradingPeriodsStore, SubmissionsStore,
|
||||
CustomColumnsStore, TableActions) {
|
||||
var TableStore = Reflux.createStore({
|
||||
init() {
|
||||
this.state = {
|
||||
students: null,
|
||||
allAssignments: null,
|
||||
assignments: null,
|
||||
submissions: null,
|
||||
toolbarOptions: GradebookToolbarStore.toolbarOptions,
|
||||
gradingPeriods: null,
|
||||
assignmentGroups: null,
|
||||
error: null,
|
||||
rows: null,
|
||||
customColumns: null,
|
||||
loading: true
|
||||
};
|
||||
this.listenToMany(TableActions);
|
||||
this.listenTo(GradebookToolbarStore, this.onToolbarOptionsChanged);
|
||||
this.listenTo(StudentEnrollmentsStore, this.onEnrollmentsChanged);
|
||||
this.listenTo(AssignmentGroupsStore, this.onAssignmentGroupsChanged);
|
||||
this.listenTo(SubmissionsStore, this.onSubmissionsChanged);
|
||||
this.listenTo(GradingPeriodsStore, this.onGradingPeriodsChanged);
|
||||
this.listenTo(CustomColumnsStore, this.onCustomColumnsChanged);
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
if (this.state === undefined) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
return this.state;
|
||||
},
|
||||
|
||||
onEnterLoadingState() {
|
||||
this.state.loading = true;
|
||||
this.trigger(this.state);
|
||||
},
|
||||
|
||||
onEnrollmentsChanged(enrollmentData) {
|
||||
this.state.students = enrollmentData.data;
|
||||
this.constructTableData();
|
||||
this.trigger(this.state);
|
||||
},
|
||||
|
||||
onAssignmentGroupsChanged(assignmentGroupData) {
|
||||
var arrangeBy, assignments;
|
||||
|
||||
arrangeBy = this.state.toolbarOptions.arrangeColumnsBy;
|
||||
assignments = _.chain(assignmentGroupData.data)
|
||||
.map(assignmentGroup => assignmentGroup.assignments)
|
||||
.flatten()
|
||||
.reject(assignment => _.contains(assignment.submission_types, 'attendence'))
|
||||
.filter(assignment => assignment.published)
|
||||
.value();
|
||||
|
||||
this.state.allAssignments = _.groupBy(assignments, assignment => assignment.id);
|
||||
this.state.assignments = this.state.allAssignments
|
||||
this.state.assignmentGroups = _.map(assignmentGroupData.data, group => {
|
||||
group.columnId = 'assignment_group_' + group.id;
|
||||
return group;
|
||||
});
|
||||
this.constructTableData();
|
||||
this.filterAssignmentsByPeriod();
|
||||
this.trigger(this.state);
|
||||
},
|
||||
|
||||
filterAssignmentsByPeriod() {
|
||||
var assignments;
|
||||
|
||||
if (this.state.allAssignments === null || this.state.gradingPeriods === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
assignments = _.flatten(_.values(this.state.allAssignments));
|
||||
assignments = _.filter(assignments, assignment =>
|
||||
GradingPeriodsStore.assignmentIsInPeriod(assignment, GradingPeriodsStore.selected()));
|
||||
|
||||
this.state.assignments = assignments;
|
||||
},
|
||||
|
||||
onSubmissionsChanged(submissionsData) {
|
||||
var submissions;
|
||||
submissions = _.groupBy(submissionsData.data, submission => submission.user_id);
|
||||
this.state.submissions = submissions;
|
||||
this.constructTableData();
|
||||
this.trigger(this.state);
|
||||
},
|
||||
|
||||
onToolbarOptionsChanged(toolbarOptionsData) {
|
||||
this.state.toolbarOptions = toolbarOptionsData;
|
||||
this.trigger(this.state);
|
||||
},
|
||||
|
||||
onGradingPeriodsChanged(gradingPeriodsData) {
|
||||
this.state.gradingPeriods = gradingPeriodsData
|
||||
this.filterAssignmentsByPeriod();
|
||||
this.trigger(this.state);
|
||||
},
|
||||
|
||||
onCustomColumnsChanged(customColumnsData) {
|
||||
if (customColumnsData.error) {
|
||||
this.state.error = customColumnsData.error;
|
||||
}
|
||||
|
||||
this.state.customColumns = customColumnsData;
|
||||
this.constructTableData();
|
||||
this.trigger(this.state);
|
||||
},
|
||||
|
||||
constructTableData() {
|
||||
var students, assignments, submissions, assignmentGroups, customColumns;
|
||||
|
||||
students = this.state.students;
|
||||
assignments = this.state.assignments;
|
||||
submissions = this.state.submissions;
|
||||
assignmentGroups = this.state.assignmentGroups;
|
||||
customColumns = this.state.customColumns;
|
||||
|
||||
if (students && assignments && submissions && customColumns) {
|
||||
this.state.rows = _.map(students, student => {
|
||||
var displayName, rowData, userSubmissions, teacherNote;
|
||||
|
||||
displayName = GradebookConstants.list_students_by_sortable_name_enabled ?
|
||||
student.user.sortable_name : student.user.name;
|
||||
|
||||
userSubmissions = _.flatten(_.map(submissions[student.user_id], s => s.submissions));
|
||||
userSubmissions = _.groupBy(userSubmissions, s => s.assignment_id);
|
||||
teacherNote = _.find(
|
||||
customColumns.teacherNotes,
|
||||
note => note.user_id === student.user_id
|
||||
);
|
||||
|
||||
rowData = {
|
||||
studentName: displayName,
|
||||
submissions: userSubmissions,
|
||||
assignmentGroups: assignmentGroups,
|
||||
student: student,
|
||||
isConcluded: student.enrollment_state === "completed",
|
||||
isInactive: student.enrollment_state === "inactive",
|
||||
teacherNote: teacherNote ? teacherNote.content : ''
|
||||
}
|
||||
|
||||
return rowData;
|
||||
});
|
||||
this.state.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return TableStore;
|
||||
});
|
|
@ -1,71 +0,0 @@
|
|||
define([
|
||||
'react',
|
||||
'underscore',
|
||||
'../components/gridCell',
|
||||
'../components/column_types/studentNameColumn',
|
||||
'../components/column_types/notesColumn',
|
||||
'../components/column_types/assignmentPercentage',
|
||||
'../components/column_types/assignmentPassFail',
|
||||
'../components/column_types/assignmentLetterGrade',
|
||||
'../components/column_types/assignmentPoints',
|
||||
'../components/column_types/totalColumn',
|
||||
'../components/column_types/assignmentGroupColumn',
|
||||
'../components/column_types/customColumn',
|
||||
'i18n!gradebook',
|
||||
'../constants'
|
||||
], function(
|
||||
React,
|
||||
_,
|
||||
GridCell,
|
||||
StudentNameColumn,
|
||||
NotesColumn,
|
||||
AssignmentPercentColumn,
|
||||
AssignmentPassFailColumn,
|
||||
AssignmentLetterGradeColumn,
|
||||
AssignmentPointsColumn,
|
||||
TotalColumn,
|
||||
AssignmentGroupColumn,
|
||||
CustomColumn,
|
||||
I18n,
|
||||
GradebookConstants
|
||||
) {
|
||||
|
||||
var cellIndex = 0;
|
||||
|
||||
var renderers = {};
|
||||
renderers[GradebookConstants.STUDENT_COLUMN_ID] = StudentNameColumn;
|
||||
renderers[GradebookConstants.NOTES_COLUMN_ID] = NotesColumn;
|
||||
renderers[GradebookConstants.PERCENT_COLUMN_ID] = AssignmentPercentColumn;
|
||||
renderers[GradebookConstants.PASS_FAIL_COLUMN_ID] = AssignmentPassFailColumn;
|
||||
renderers[GradebookConstants.GPA_SCALE_COLUMN_ID] = AssignmentLetterGradeColumn;
|
||||
renderers[GradebookConstants.LETTER_GRADE_COLUMN_ID] = AssignmentLetterGradeColumn;
|
||||
renderers[GradebookConstants.POINTS_COLUMN_ID] = AssignmentPointsColumn;
|
||||
renderers[GradebookConstants.TOTAL_COLUMN_ID] = TotalColumn;
|
||||
renderers[GradebookConstants.ASSIGNMENT_GROUP_COLUMN_ID] = AssignmentGroupColumn;
|
||||
renderers[GradebookConstants.CUSTOM_COLUMN_ID] = CustomColumn;
|
||||
|
||||
function getRenderer (cellData, cellDataKey, rowData, rowIndex, columnData) {
|
||||
var Renderer = renderers[columnData.columnType];
|
||||
|
||||
if (Renderer) {
|
||||
var key = columnData.columnType + cellDataKey;
|
||||
return (<GridCell
|
||||
activeCell={columnData.activeCell}
|
||||
cellIndex={cellIndex++}
|
||||
columnData={columnData}
|
||||
cellData={cellData}
|
||||
key={key}
|
||||
renderer={Renderer}
|
||||
rowData={rowData}
|
||||
setActiveCell={columnData.setActiveCell}/>);
|
||||
} else {
|
||||
var message = 'Cell Renderer Not Registered. ' +
|
||||
'Register "' + columnData.columnType +
|
||||
'" in the renderers Object (columnRenderer.jsx)';
|
||||
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
|
||||
return {getRenderer: getRenderer};
|
||||
});
|
|
@ -1,14 +0,0 @@
|
|||
define([
|
||||
'react',
|
||||
'../components/column_types/headerRenderer'
|
||||
], function(React, HeaderRenderer) {
|
||||
|
||||
var getHeader = function(label, cellDataKey, columnData, _, width) {
|
||||
return (
|
||||
<HeaderRenderer key={cellDataKey + '_header'} label={label}
|
||||
columnData={columnData} width={width}/>
|
||||
);
|
||||
};
|
||||
|
||||
return { getHeader: getHeader };
|
||||
});
|
|
@ -0,0 +1,7 @@
|
|||
define(function () {
|
||||
const constants = {
|
||||
MAX_NOTE_LENGTH: 255
|
||||
};
|
||||
|
||||
return constants;
|
||||
});
|
|
@ -0,0 +1,66 @@
|
|||
define([
|
||||
'underscore'
|
||||
], function (_) {
|
||||
const getDueDateFromAssignment = function (assignment) {
|
||||
if (assignment.due_at) {
|
||||
return new Date(assignment.due_at);
|
||||
}
|
||||
const overrides = assignment.overrides;
|
||||
if (!overrides || overrides.length > 1) { return null }
|
||||
const overrideWithDueAt = _.find(overrides, override => override.due_at);
|
||||
return overrideWithDueAt ? new Date(overrideWithDueAt.due_at) : null;
|
||||
};
|
||||
|
||||
const assignmentHelper = {
|
||||
compareByDueDate (a, b) {
|
||||
let aDate = getDueDateFromAssignment(a);
|
||||
let bDate = getDueDateFromAssignment(b);
|
||||
const aDateIsNull = _.isNull(aDate);
|
||||
const bDateIsNull = _.isNull(bDate);
|
||||
if (aDateIsNull && !bDateIsNull) { return 1 }
|
||||
if (!aDateIsNull && bDateIsNull) { return -1 }
|
||||
if (aDateIsNull && bDateIsNull) {
|
||||
if (this.hasMultipleDueDates(a) && !this.hasMultipleDueDates(b)) { return -1 }
|
||||
if (!this.hasMultipleDueDates(a) && this.hasMultipleDueDates(b)) { return 1 }
|
||||
}
|
||||
aDate = +aDate;
|
||||
bDate = +bDate;
|
||||
if (aDate === bDate) {
|
||||
const aName = a.name.toLowerCase();
|
||||
const bName = b.name.toLowerCase();
|
||||
if (aName === bName) { return 0 }
|
||||
return aName > bName ? 1 : -1;
|
||||
}
|
||||
return aDate - bDate;
|
||||
},
|
||||
|
||||
hasMultipleDueDates (assignment) {
|
||||
return !!(
|
||||
assignment.has_overrides &&
|
||||
assignment.overrides &&
|
||||
assignment.overrides.length > 1
|
||||
);
|
||||
},
|
||||
|
||||
getComparator (arrangeBy) {
|
||||
if (arrangeBy === 'due_date') {
|
||||
return this.compareByDueDate.bind(this);
|
||||
}
|
||||
if (arrangeBy === 'assignment_group') {
|
||||
return this.compareByAssignmentGroup.bind(this);
|
||||
}
|
||||
},
|
||||
|
||||
compareByAssignmentGroup (a, b) {
|
||||
const diffOfAssignmentGroupPosition = a.assignment_group_position - b.assignment_group_position;
|
||||
if (diffOfAssignmentGroupPosition === 0) {
|
||||
const diffOfAssignmentPosition = a.position - b.position;
|
||||
if (diffOfAssignmentPosition === 0) { return 0 }
|
||||
return diffOfAssignmentPosition;
|
||||
}
|
||||
return diffOfAssignmentGroupPosition;
|
||||
}
|
||||
};
|
||||
|
||||
return assignmentHelper;
|
||||
});
|
|
@ -3,4 +3,3 @@
|
|||
@import "pages/gradebook2/learning_outcome_gradebook.scss";
|
||||
@import "pages/gradebook2/gradebook2.scss";
|
||||
@import "pages/gradebook2/grade_passback.scss";
|
||||
@import "vendor/fixed-data-table.scss";
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
@import "pages/shared/message_students.scss";
|
||||
@import "base/environment";
|
||||
@import "pages/gradebook2/grade_passback.scss";
|
||||
@import "pages/gradebook2/learning_outcome_gradebook.scss";
|
||||
@import "pages/react_gradebook/react_gradebook.scss";
|
||||
@import "vendor/fixed-data-table.scss";
|
|
@ -1,595 +0,0 @@
|
|||
$gradebook_letter-grade-font: lighten($ic-color-dark, 8%);
|
||||
$gradebook_even-row-color: lighten($ic-color-neutral, 6);
|
||||
$gradebook_border-color: darken($ic-color-neutral, 7);
|
||||
$gradebook_late-color: saturate(darken($ic-bg-light-danger, 5), 20);
|
||||
$gradebook_resubmitted-color: desaturate(darken($ic-bg-light-alert, 7), 2);
|
||||
|
||||
.react-gradebook {
|
||||
.teacher-note {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
// pretty much everything in this file should eventually
|
||||
// go within .react-gradebook, but the notes styling is here
|
||||
// because the notes modal attaches to the 'application'
|
||||
// div outside of the .react-gradebook
|
||||
.notes-textarea {
|
||||
width: 95%;
|
||||
height: 60%;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.notes-body {
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
a#accessibility_warning {
|
||||
@include accessibility-prompt;
|
||||
@include fontSize(14px);
|
||||
}
|
||||
|
||||
#footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#main {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
$cell_height: 33px;
|
||||
|
||||
.gradebook2 .ic-layout-contentMain {
|
||||
position: relative;
|
||||
padding: 0 $ic-sp*2;
|
||||
}
|
||||
|
||||
#gradebook-grid-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.student-name {
|
||||
color: #1b7eda;
|
||||
text-shadow: #fbf8f8 0 0 1px;
|
||||
}
|
||||
|
||||
.student-grades-link {
|
||||
@if $use_high_contrast { color: darken($ic-link-color, 5%); }
|
||||
}
|
||||
|
||||
.meta-cell .avatar {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
margin-top: 3px;
|
||||
margin-right: 4px;
|
||||
float: left;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.student-placeholder {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.student-section {
|
||||
margin-top: -7px;
|
||||
@include fontSize(12px);
|
||||
}
|
||||
|
||||
.secondary_identifier_cell, .custom_column {
|
||||
color: #333333;
|
||||
@include fontSize(12px);
|
||||
text-align: center;
|
||||
line-height: 35px;
|
||||
}
|
||||
|
||||
// show and hide student names using a parent css class.
|
||||
.hide-students {
|
||||
.student-name, .avatar {
|
||||
display: none;
|
||||
}
|
||||
.student-placeholder {
|
||||
display: block;
|
||||
}
|
||||
.secondary_identifier_cell {
|
||||
text-indent: -9999em;
|
||||
}
|
||||
}
|
||||
|
||||
.gradebook-header-column {
|
||||
background-color: #f3f4f8;
|
||||
background-image: none;
|
||||
padding: 10px;
|
||||
@include fontSize(12px);
|
||||
text-align: center;
|
||||
font-weight: normal;
|
||||
// override jqueryUI style
|
||||
&.ui-state-default {
|
||||
height: 30px;
|
||||
}
|
||||
&.hovered-column,
|
||||
&.ui-state-hover {
|
||||
@if $use_high_contrast { @include vertical-gradient(#eef5fc, #d5e2ed); }
|
||||
@else { @include vertical-gradient(#e0ecf9, #bdd2e3); }
|
||||
}
|
||||
a.assignment-name {
|
||||
font-weight: normal;
|
||||
|
||||
text-shadow: white 0 0 1px;
|
||||
//this is for the ellipses
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
@if $use_high_contrast {
|
||||
&:hover, &:focus { text-decoration: underline; }
|
||||
}
|
||||
@else { color: $ic-link-color; }
|
||||
}
|
||||
.muted {
|
||||
background: url("/images/icon-sound-muted.svg") no-repeat left center;
|
||||
background-size: 16px;
|
||||
padding-left: 18px;
|
||||
}
|
||||
.assignment-due-date {
|
||||
position: relative;
|
||||
bottom: 4px;
|
||||
@include fontSize(12px);
|
||||
// If high contrast, default to regular dark text
|
||||
@if $use_high_contrast == false { color: lighten($ic-font-color-dark, 15%); }
|
||||
}
|
||||
.gradebook-header-drop {
|
||||
//put it above the "out of"
|
||||
z-index: 1;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
position: absolute;
|
||||
outline: none;
|
||||
right: 3px;
|
||||
bottom: 3px;
|
||||
text-indent: 999px;
|
||||
opacity: 0;
|
||||
//override jqueryui blue border
|
||||
border: 0 none;
|
||||
|
||||
@if $use_high_contrast {
|
||||
background: url("/images/jqueryui/gradebook-header-drop2-high-contrast.png") no-repeat top left;
|
||||
@media (min--moz-device-pixel-ratio: 1.5),
|
||||
(-o-min-device-pixel-ratio: 3/2),
|
||||
(-webkit-min-device-pixel-ratio: 1.5),
|
||||
(min-device-pixel-ratio: 1.5),
|
||||
(min-resolution: 1.5dppx) {
|
||||
background-image: url("/images/jqueryui/gradebook-header-drop2-high-contrast@2x.png");
|
||||
background-size: 14px 28px;
|
||||
}
|
||||
}
|
||||
|
||||
@else {
|
||||
background: url("/images/jqueryui/gradebook-header-drop2.png") no-repeat top left;
|
||||
@media (min--moz-device-pixel-ratio: 1.5),
|
||||
(-o-min-device-pixel-ratio: 3/2),
|
||||
(-webkit-min-device-pixel-ratio: 1.5),
|
||||
(min-device-pixel-ratio: 1.5),
|
||||
(min-resolution: 1.5dppx) {
|
||||
background-image: url("/images/jqueryui/gradebook-header-drop2@2x.png");
|
||||
background-size: 14px 28px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover, &.ui-menu-trigger-menu-is-open {
|
||||
background-position: bottom left;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
&:hover .gradebook-header-drop {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.gradebook-label {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.gradebook-cell-turnitin {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
z-index: 1;
|
||||
width: 20px;
|
||||
height: 16px;
|
||||
&.no-score {
|
||||
background-image: url(/images/turnitin_no_score.png);
|
||||
}
|
||||
&.none-score {
|
||||
background-image: url(/images/turnitin_none_score.png);
|
||||
}
|
||||
&.acceptable-score {
|
||||
background-image: url(/images/turnitin_acceptable_score.png);
|
||||
}
|
||||
&.warning-score {
|
||||
background-image: url(/images/turnitin_warning_score.png);
|
||||
}
|
||||
&.problem-score {
|
||||
background-image: url(/images/turnitin_problem_score.png);
|
||||
}
|
||||
&.failure-score {
|
||||
background-image: url(/images/turnitin_failure_score.png);
|
||||
}
|
||||
}
|
||||
|
||||
.gradebook-cell-comment {
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
right: -1px;
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
z-index: 2;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
|
||||
@if $use_high_contrast {
|
||||
background: url("/images/gradebook-comments-sprite3-high-contrast.png") no-repeat 100% 0;
|
||||
@media (min--moz-device-pixel-ratio: 1.5),
|
||||
(-o-min-device-pixel-ratio: 3/2),
|
||||
(-webkit-min-device-pixel-ratio: 1.5),
|
||||
(min-device-pixel-ratio: 1.5),
|
||||
(min-resolution: 1.5dppx) {
|
||||
background-image: url("/images/gradebook-comments-sprite3-high-contrast@2x.png");
|
||||
background-size: 17px 105px;
|
||||
}
|
||||
}
|
||||
|
||||
@else {
|
||||
background: url("/images/gradebook-comments-sprite3.png") no-repeat 100% 0;
|
||||
@media (min--moz-device-pixel-ratio: 1.5),
|
||||
(-o-min-device-pixel-ratio: 3/2),
|
||||
(-webkit-min-device-pixel-ratio: 1.5),
|
||||
(min-device-pixel-ratio: 1.5),
|
||||
(min-resolution: 1.5dppx) {
|
||||
background-image: url("/images/gradebook-comments-sprite3@2x.png");
|
||||
background-size: 17px 105px;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
visibility: visible;
|
||||
background-position: 100% -41px;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
|
||||
&:hover {
|
||||
background-position: 100% -88px !important;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gradebook-cell:hover a.gradebook-cell-comment {
|
||||
visibility: visible;
|
||||
background-position: 100% -41px;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
|
||||
}
|
||||
.gradebook-cell:hover a.gradebook-cell-comment:hover {
|
||||
background-position: 100% -88px !important;
|
||||
}
|
||||
|
||||
.gradebook-cell-comment-label {
|
||||
@include hide-text;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.gradebook-cell-editable {
|
||||
height: $cell_height - 1px -8px;
|
||||
padding-top: 8px;
|
||||
margin: 0;
|
||||
border: 1px solid #35a5e5;
|
||||
background-color: white;
|
||||
box-shadow: 0 0 5px rgba(81, 203, 238, 1);
|
||||
}
|
||||
|
||||
.gradebook-cell {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
.grade {
|
||||
border: none;
|
||||
text-align: center;
|
||||
outline: none;
|
||||
@include fontSize(12px);
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background: none;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.grade::-webkit-outer-spin-button {
|
||||
display: none;
|
||||
}
|
||||
.grade::-ms-clear {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.points-input {
|
||||
.out-of-grade {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.out-of-float {
|
||||
float: left;
|
||||
width: 50%;
|
||||
|
||||
.divider {
|
||||
padding: 0 2px 0 2px;
|
||||
}
|
||||
|
||||
.grade::-webkit-outer-spin-button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.out-of-points {
|
||||
padding-top: 1px;
|
||||
text-align: left;
|
||||
@if $use_high_contrast == false { color: #888888; }
|
||||
}
|
||||
}
|
||||
|
||||
$gradebook_checkbox_width: 16px;
|
||||
|
||||
.gradebook-checkbox {
|
||||
@include hide-text;
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: $gradebook_checkbox_width;
|
||||
height: $gradebook_checkbox_width;
|
||||
margin-top: -$gradebook_checkbox_width / 2;
|
||||
margin-left: -$gradebook_checkbox_width / 2;
|
||||
background: url("/images/checkbox_sprite3.png") no-repeat 0 0;
|
||||
&.gradebook-checkbox-complete {
|
||||
background-position: -48px 0;
|
||||
}
|
||||
&.gradebook-checkbox-incomplete {
|
||||
background-position: -64px 0;
|
||||
}
|
||||
&.editable {
|
||||
&.gradebook-checkbox-complete {
|
||||
background-position: -16px 0;
|
||||
}
|
||||
&.gradebook-checkbox-incomplete {
|
||||
background-position: -32px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#hide_warning {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.minimized {
|
||||
background-image: url("/images/4_percent_opacity.png");
|
||||
.gradebook-cell {
|
||||
@include fontSize(0px);
|
||||
* {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#gradebook-toolbar {
|
||||
position: relative;
|
||||
background-color: transparent;
|
||||
border: transparent;
|
||||
padding: $ic-sp;
|
||||
border-top: none;
|
||||
@include breakpoint(desktop) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.gradebook_dropdowns {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.gradebook_filter {
|
||||
display: inline-block;
|
||||
input {
|
||||
width: 248px;
|
||||
}
|
||||
@include breakpoint(desktop) {
|
||||
margin-left: $ic-sp/3;
|
||||
}
|
||||
}
|
||||
|
||||
.gradebook_menu {
|
||||
margin-top: $ic-sp/3;
|
||||
@include breakpoint(desktop) {
|
||||
white-space: nowrap;
|
||||
margin-top: 0;
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin gradebook_menu_label($fontsize) {
|
||||
@include fontSize($fontsize);
|
||||
font-weight: bold;
|
||||
margin-bottom: 0;
|
||||
vertical-align: 0px !important;
|
||||
position: relative;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
#section-to-show-menu {
|
||||
width: 350px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
max-height: 275px;
|
||||
label {
|
||||
@include gradebook_menu_label(12px);
|
||||
}
|
||||
.ui-state-focus:last-child {
|
||||
margin: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.post-grades-placeholder {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.submission_type_icon {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 22px;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
.online_text_entry & {
|
||||
background-image: url("/images/text_entry.png");
|
||||
}
|
||||
.discussion_topic & {
|
||||
background-image: url("/images/word_bubble.png");
|
||||
}
|
||||
.online_upload & {
|
||||
background-image: url("/images/file.png");
|
||||
}
|
||||
.pending_review & {
|
||||
background-image: url("/images/pending_review.png");
|
||||
}
|
||||
.media_comment &, .media_recording & {
|
||||
background-image: url("/images/media_comment.gif");
|
||||
}
|
||||
.quiz & {
|
||||
background-image: url("/images/quiz.png");
|
||||
}
|
||||
}
|
||||
|
||||
.letter-grade-points,
|
||||
.gpa-scale-points {
|
||||
position: absolute;
|
||||
@include fontSize(12px);
|
||||
padding-left: 8px;
|
||||
line-height: 19px;
|
||||
color: $gradebook_letter-grade-font;
|
||||
}
|
||||
|
||||
.final-warning {
|
||||
margin-left: -16px;
|
||||
}
|
||||
|
||||
.gradebook_dropdown, .export_dropdown {
|
||||
display:none;
|
||||
li, label {
|
||||
@include gradebook_menu_label(13px);
|
||||
cursor:pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-menu .ui-menu-item.ui-state-disabled {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.ui-menu-item.ui-state-disabled label {
|
||||
margin-bottom: 0;
|
||||
vertical-align: 0;
|
||||
}
|
||||
|
||||
.post-grades-menu {
|
||||
li.external-tools-dialog,
|
||||
li.post-grades-placeholder {
|
||||
cursor: pointer;
|
||||
}
|
||||
li.external-tools-dialog.ellip {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
// Very specific declaration to get Choose a Section text to have better text:background contrast in Section dropdown
|
||||
#section-to-show-menu {
|
||||
li:first-of-type.ui-state-disabled {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
// Fixed Data Table overrides
|
||||
|
||||
#gradebook_grid {
|
||||
[data-reactid$="$header"] {
|
||||
box-shadow: 0px 1px 4px rgba(28, 28, 28, 0.2);
|
||||
}
|
||||
|
||||
.gradebook-cell{
|
||||
overflow-x: hidden;
|
||||
white-space: nowrap;
|
||||
height: 36px;
|
||||
box-sizing: border-box;
|
||||
margin-right: 1px;
|
||||
padding: 5px;
|
||||
border: 1px solid transparent;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid $gradebook_border_color;
|
||||
|
||||
.student-name {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&.active {
|
||||
border-color: $ic-brand-primary !important;
|
||||
}
|
||||
|
||||
&.late {
|
||||
background-color: $gradebook_late-color;
|
||||
}
|
||||
&.resubmitted {
|
||||
background-color: $gradebook_resubmitted-color;
|
||||
}
|
||||
&.grayed-out {
|
||||
background-color: $grayLighter;
|
||||
}
|
||||
}
|
||||
|
||||
.assignment-grade-cell {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
|
||||
.gradebook-header-column {
|
||||
background-color: $gradebook_even-row-color;
|
||||
height: 39px;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
&.title {
|
||||
padding-top: 8px;
|
||||
font-weight:bold;
|
||||
text-shadow: white 0 0 1px;
|
||||
|
||||
span {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.public_fixedDataTableRow_highlighted,
|
||||
.public_fixedDataTableRow_highlighted .public_fixedDataTableCell_main {
|
||||
background-color: $gradebook_even-row-color !important;
|
||||
}
|
||||
}
|
||||
|
||||
#gradebook-toolbar {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
#react-gradebook-canvas {
|
||||
outline: none;
|
||||
}
|
|
@ -1,514 +0,0 @@
|
|||
/**
|
||||
* FixedDataTable v0.5.0
|
||||
*
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule fixedDataTableCellGroupLayout
|
||||
*/
|
||||
|
||||
.fixedDataTableCellGroupLayout_cellGroup {
|
||||
-webkit-backface-visibility: hidden;
|
||||
backface-visibility: hidden;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.fixedDataTableCellGroupLayout_cellGroup > .public_fixedDataTableCell_main {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.fixedDataTableCellGroupLayout_cellGroupWrapper {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
/**
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule fixedDataTableCellLayout
|
||||
*/
|
||||
|
||||
.fixedDataTableCellLayout_main {
|
||||
border-right-style: solid;
|
||||
border-right-width: 1px;
|
||||
border-width: 0 1px 0 0;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.fixedDataTableCellLayout_lastChild {
|
||||
border-width: 0 1px 1px 0;
|
||||
}
|
||||
|
||||
.fixedDataTableCellLayout_alignRight {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.fixedDataTableCellLayout_alignCenter {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fixedDataTableCellLayout_wrap1 {
|
||||
display: table;
|
||||
}
|
||||
|
||||
.fixedDataTableCellLayout_wrap2 {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.fixedDataTableCellLayout_wrap3 {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.fixedDataTableCellLayout_columnResizerContainer {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
width: 6px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.fixedDataTableCellLayout_columnResizerContainer:hover {
|
||||
cursor: ew-resize;
|
||||
}
|
||||
|
||||
.fixedDataTableCellLayout_columnResizerContainer:hover .fixedDataTableCellLayout_columnResizerKnob {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.fixedDataTableCellLayout_columnResizerKnob {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
visibility: hidden;
|
||||
width: 4px;
|
||||
}
|
||||
/**
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule fixedDataTableColumnResizerLineLayout
|
||||
*/
|
||||
|
||||
.fixedDataTableColumnResizerLineLayout_mouseArea {
|
||||
cursor: ew-resize;
|
||||
position: absolute;
|
||||
right: -5px;
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
.fixedDataTableColumnResizerLineLayout_main {
|
||||
border-right-style: solid;
|
||||
border-right-width: 1px;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
body[dir="rtl"] .fixedDataTableColumnResizerLineLayout_main {
|
||||
/* the resizer line is in the wrong position in RTL with no easy fix.
|
||||
* Disabling is more useful than displaying it.
|
||||
* #167 (github) should look into this and come up with a permanent fix.
|
||||
*/
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.fixedDataTableColumnResizerLineLayout_hiddenElem {
|
||||
display: none !important;
|
||||
}
|
||||
/**
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule fixedDataTableLayout
|
||||
*/
|
||||
|
||||
.fixedDataTableLayout_main {
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.fixedDataTableLayout_header,
|
||||
.fixedDataTableLayout_hasBottomBorder {
|
||||
border-bottom-style: solid;
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
.fixedDataTableLayout_footer .public_fixedDataTableCell_main {
|
||||
border-top-style: solid;
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
||||
.fixedDataTableLayout_topShadow,
|
||||
.fixedDataTableLayout_bottomShadow {
|
||||
height: 4px;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.fixedDataTableLayout_bottomShadow {
|
||||
margin-top: -4px;
|
||||
}
|
||||
|
||||
.fixedDataTableLayout_rowsContainer {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.fixedDataTableLayout_horizontalScrollbar {
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
}
|
||||
/**
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule fixedDataTableRowLayout
|
||||
*/
|
||||
|
||||
.fixedDataTableRowLayout_main {
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.fixedDataTableRowLayout_body {
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.fixedDataTableRowLayout_fixedColumnsDivider {
|
||||
-webkit-backface-visibility: hidden;
|
||||
backface-visibility: hidden;
|
||||
border-left-style: solid;
|
||||
border-left-width: 1px;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.fixedDataTableRowLayout_columnsShadow {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
.fixedDataTableRowLayout_rowWrapper {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
/**
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ScrollbarLayout
|
||||
*/
|
||||
|
||||
.ScrollbarLayout_main {
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
-webkit-transition-duration: 250ms;
|
||||
transition-duration: 250ms;
|
||||
-webkit-transition-timing-function: ease;
|
||||
transition-timing-function: ease;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.ScrollbarLayout_mainVertical {
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
-webkit-transition-property: background-color width;
|
||||
transition-property: background-color width;
|
||||
width: 15px;
|
||||
}
|
||||
|
||||
.ScrollbarLayout_mainVertical.public_Scrollbar_mainActive,
|
||||
.ScrollbarLayout_mainVertical:hover {
|
||||
width: 17px;
|
||||
}
|
||||
|
||||
.ScrollbarLayout_mainHorizontal {
|
||||
bottom: 0;
|
||||
height: 15px;
|
||||
left: 0;
|
||||
-webkit-transition-property: background-color height;
|
||||
transition-property: background-color height;
|
||||
}
|
||||
|
||||
/* Touching the scroll-track directly makes the scroll-track bolder */
|
||||
.ScrollbarLayout_mainHorizontal.public_Scrollbar_mainActive,
|
||||
.ScrollbarLayout_mainHorizontal:hover {
|
||||
height: 17px;
|
||||
}
|
||||
|
||||
.ScrollbarLayout_face {
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* This selector renders the "nub" of the scrollface. The nub must
|
||||
* be rendered as pseudo-element so that it won't receive any UI events then
|
||||
* we can get the correct `event.offsetX` and `event.offsetY` from the
|
||||
* scrollface element while dragging it.
|
||||
*/
|
||||
.ScrollbarLayout_face:after {
|
||||
border-radius: 6px;
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
-webkit-transition: background-color 250ms ease;
|
||||
transition: background-color 250ms ease;
|
||||
}
|
||||
|
||||
.ScrollbarLayout_faceHorizontal {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.ScrollbarLayout_faceHorizontal:after {
|
||||
bottom: 4px;
|
||||
left: 0;
|
||||
top: 4px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ScrollbarLayout_faceVertical {
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.ScrollbarLayout_faceVertical:after {
|
||||
height: 100%;
|
||||
left: 4px;
|
||||
right: 4px;
|
||||
top: 0;
|
||||
}
|
||||
/**
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule fixedDataTable
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Table.
|
||||
*/
|
||||
.public_fixedDataTable_main {
|
||||
border-color: #d3d3d3;
|
||||
}
|
||||
|
||||
.public_fixedDataTable_header,
|
||||
.public_fixedDataTable_hasBottomBorder {
|
||||
border-color: #d3d3d3;
|
||||
}
|
||||
|
||||
.public_fixedDataTable_header .public_fixedDataTableCell_main {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.public_fixedDataTable_header,
|
||||
.public_fixedDataTable_header .public_fixedDataTableCell_main {
|
||||
background-color: #f6f7f8;
|
||||
background-image: -webkit-linear-gradient(#fff, #efefef);
|
||||
background-image: linear-gradient(#fff, #efefef);
|
||||
}
|
||||
|
||||
.public_fixedDataTable_footer .public_fixedDataTableCell_main {
|
||||
background-color: #f6f7f8;
|
||||
border-color: #d3d3d3;
|
||||
}
|
||||
|
||||
.public_fixedDataTable_topShadow {
|
||||
background: 0 0 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAECAYAAABP2FU6AAAAF0lEQVR4AWPUkNeSBhHCjJoK2twgFisAFagCCp3pJlAAAAAASUVORK5CYII=) repeat-x;
|
||||
}
|
||||
|
||||
.public_fixedDataTable_bottomShadow {
|
||||
background: 0 0 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAECAYAAABP2FU6AAAAHElEQVQI12MwNjZmZdAT1+Nm0JDWEGZQk1GTBgAWkwIeAEp52AAAAABJRU5ErkJggg==) repeat-x;
|
||||
}
|
||||
|
||||
.public_fixedDataTable_horizontalScrollbar .public_Scrollbar_mainHorizontal {
|
||||
background-color: #fff;
|
||||
}
|
||||
/**
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule fixedDataTableCell
|
||||
*/
|
||||
|
||||
/**
|
||||
* Table cell.
|
||||
*/
|
||||
.public_fixedDataTableCell_main {
|
||||
background-color: #fff;
|
||||
border-color: #d3d3d3;
|
||||
}
|
||||
|
||||
.public_fixedDataTableCell_highlighted {
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
|
||||
.public_fixedDataTableCell_cellContent {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.public_fixedDataTableCell_columnResizerKnob {
|
||||
background-color: #0284ff;
|
||||
}
|
||||
/**
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule fixedDataTableColumnResizerLine
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Column resizer line.
|
||||
*/
|
||||
.public_fixedDataTableColumnResizerLine_main {
|
||||
border-color: #0284ff;
|
||||
}
|
||||
/**
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule fixedDataTableRow
|
||||
*/
|
||||
|
||||
/**
|
||||
* Table row.
|
||||
*/
|
||||
.public_fixedDataTableRow_main {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.public_fixedDataTableRow_highlighted,
|
||||
.public_fixedDataTableRow_highlighted .public_fixedDataTableCell_main {
|
||||
background-color: #f6f7f8;
|
||||
}
|
||||
|
||||
.public_fixedDataTableRow_fixedColumnsDivider {
|
||||
border-color: #d3d3d3;
|
||||
}
|
||||
|
||||
.public_fixedDataTableRow_columnsShadow {
|
||||
background: 0 0 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAABCAYAAAD5PA/NAAAAFklEQVQIHWPSkNeSBmJhTQVtbiDNCgASagIIuJX8OgAAAABJRU5ErkJggg==) repeat-y;
|
||||
}
|
||||
/**
|
||||
* Copyright (c) 2015, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule Scrollbar
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Scrollbars.
|
||||
*/
|
||||
|
||||
/* Touching the scroll-track directly makes the scroll-track bolder */
|
||||
.public_Scrollbar_main.public_Scrollbar_mainActive,
|
||||
.public_Scrollbar_main:hover {
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.public_Scrollbar_main.public_Scrollbar_mainActive,
|
||||
.public_Scrollbar_main:hover {
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.public_Scrollbar_mainOpaque,
|
||||
.public_Scrollbar_mainOpaque.public_Scrollbar_mainActive,
|
||||
.public_Scrollbar_mainOpaque:hover {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.public_Scrollbar_face:after {
|
||||
background-color: #c2c2c2;
|
||||
}
|
||||
|
||||
.public_Scrollbar_main:hover .public_Scrollbar_face:after,
|
||||
.public_Scrollbar_mainActive .public_Scrollbar_face:after,
|
||||
.public_Scrollbar_faceActive:after {
|
||||
background-color: #7d7d7d;
|
||||
}
|
|
@ -66,12 +66,12 @@
|
|||
<div id="gradebook-toolbar" class="toolbar">
|
||||
<div class="gradebook_dropdowns">
|
||||
<% if multiple_grading_periods? %>
|
||||
<span id="mgp-dropdown" class="multiple-grading-periods-selector-placeholder"></span>
|
||||
<span class="multiple-grading-periods-selector-placeholder"></span>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="gradebook_filter" style="display:none">
|
||||
<% placeholder = t('Filter by student name or secondary ID') %>
|
||||
<input type="text" class="search-query" id="gradebook-filter-input" placeholder="<%= placeholder %>" aria-label="<%= placeholder %>">
|
||||
<input type="text" class="search-query" placeholder="<%= placeholder %>" aria-label="<%= placeholder %>">
|
||||
</div>
|
||||
<div class="gradebook_menu">
|
||||
<span class="ui-buttonset">
|
||||
|
|
|
@ -1,163 +0,0 @@
|
|||
|
||||
<%
|
||||
content_for :page_title, "Gradebook - #{@context.name}"
|
||||
@body_classes << "gradebook2 full-width"
|
||||
@show_left_side = false
|
||||
@show_embedded_chat = false
|
||||
css_bundle :react_gradebook
|
||||
js_bundle :gradebook2, :react_gradebook
|
||||
%>
|
||||
<div id="keyboard_navigation"></div>
|
||||
<div id="gradebook_wrapper" class="react-gradebook">
|
||||
<h1 class="screenreader-only"><%= t('page_header_title', 'Gradebook') %></h1>
|
||||
<div class="gradebook-header">
|
||||
<div class="accessibility_warning">
|
||||
<%= link_to t(:accessibility_warning, 'Warning: For improved accessibility, please click here to use the Individual View Gradebook.'), context_url(@context, :change_gradebook_version_context_gradebook_url, :version => "srgb"), :id => "accessibility_warning", :class => "screenreader-only" %>
|
||||
</div>
|
||||
<% if @context.feature_enabled?(:outcome_gradebook) || @context.feature_enabled?(:post_grades) %>
|
||||
<nav class="gradebook-navigation">
|
||||
<ul class="nav nav-pills gradebook-navigation-pills">
|
||||
<li class="active">
|
||||
<a href="#" data-id="assignment"><%= t(:grades, "Grades") %></a>
|
||||
</li>
|
||||
<% if @context.feature_enabled?(:outcome_gradebook) %>
|
||||
<li>
|
||||
<a href="#" data-id="outcome"><%= t(:learning_mastery, "Learning Mastery") %></a>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</nav>
|
||||
<% end %>
|
||||
<div class="header-buttons">
|
||||
<a href="<%= context_url(@context, :change_gradebook_version_context_gradebook_url, :version => "srgb") %>" class="Button individual-view-button" id="change_gradebook_version_link_holder" type="button"><%= t(:individual_view, "Individual View") %></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="assignment-gradebook-container">
|
||||
<div id="gradebook-toolbar" class="toolbar">
|
||||
<div class="gradebook_dropdowns">
|
||||
<% if !@context.feature_enabled?(:outcome_gradebook) && !@context.feature_enabled?(:post_grades)%>
|
||||
<span class="section-button-placeholder"></span>
|
||||
<% end %>
|
||||
<% if multiple_grading_periods? %>
|
||||
<span class="multiple-grading-periods-selector-placeholder"></span>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="gradebook_filter">
|
||||
<% placeholder = t('filter_by_student', 'Filter by student name or secondary ID') %>
|
||||
<input type="text" id="gradebook-filter-input" class="search-query" placeholder="<%= placeholder %>" aria-label="<%= placeholder %>">
|
||||
</div>
|
||||
<div class="gradebook_menu">
|
||||
<span class="ui-buttonset">
|
||||
|
||||
<% if @post_grades_tools.count > 1 %>
|
||||
<button id="post_grades" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only"><%= t('Post Grades') %></button>
|
||||
<ul style="display: none;" class="post-grades-menu ui-kyle-menu">
|
||||
<% @post_grades_tools.each do |tool| %>
|
||||
<% case tool[:type] %>
|
||||
<% when :lti %>
|
||||
<li class="external-tools-dialog"><a aria-controls="post_grades_frame_dialog" role="button" data-url="<%= tool[:data_url]%>"><%= tool[:name]%></a></li>
|
||||
<% when :post_grades %>
|
||||
<li class="post-grades-placeholder in-menu"></li>
|
||||
<% when :ellip %>
|
||||
<li class="external-tools-dialog ellip"><a>…</a></li>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</ul>
|
||||
<% elsif @post_grades_tools.count == 1 %>
|
||||
<% case @post_grades_tools[0][:type] %>
|
||||
<% when :lti %>
|
||||
<button class="external-tools-dialog ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" data-url="<%= @post_grades_tools[0][:data_url]%>"><%= @post_grades_tools[0][:name]%></button>
|
||||
<% when :post_grades %>
|
||||
<span class="post-grades-placeholder"></span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% if @gradebook_is_editable && @context.allows_gradebook_uploads? %>
|
||||
<%= link_to(new_course_gradebook_upload_path(@context), class: "ui-button") do %>
|
||||
<i class="icon-import"></i>
|
||||
<%= t 'Import' %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<button class="ui-button" id="download_csv">
|
||||
<i class="icon-export"></i>
|
||||
<%= t('download_scores', 'Export') %>
|
||||
</button>
|
||||
<ul style="display: none;" id="csv_export_options" class="export_dropdown ui-kyle-menu">
|
||||
<li class="generate_new_csv"><a href="#"><%=t('export_current_gradebook', 'Current') %></a></li>
|
||||
<% if @last_exported_gradebook_csv.present? %>
|
||||
<li><a href="<%= @last_exported_gradebook_csv.attachment.download_url %>" class="open_in_a_new_tab">
|
||||
<%= t('Previous (%{time})',time: datetime_string(@last_exported_gradebook_csv.attachment.updated_at)) %>
|
||||
</a></li>
|
||||
<% else %>
|
||||
<li style="display:none;">
|
||||
<a class="open_in_a_new_tab"></a>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</span>
|
||||
<iframe id="csv_download" style="display:none"></iframe>
|
||||
|
||||
<button id="gradebook_settings"><i class="icon-settings"></i></button>
|
||||
<ul id="settings_dropdown" style="display: none;" class="gradebook_drop_down ui-kyle-menu">
|
||||
<li>
|
||||
<a href="<%= context_url(@context, :context_gradebook_url) %>/history">
|
||||
<%= t('view_grading_history', 'View Grading History') %>
|
||||
</a>
|
||||
</li>
|
||||
<% if @context.allows_grade_publishing_by(@current_user) && can_do(@context, @current_user, :manage_grades) %>
|
||||
<li>
|
||||
<a id="publish_to_sis" href="<%= context_url(@context, :context_details_url, :anchor => 'tab-grade-publishing') %>">
|
||||
<%= t('publish_to_sis', 'Publish grades to SIS') %>
|
||||
</a>
|
||||
</li>
|
||||
<% end %>
|
||||
<% if @gradebook_is_editable %>
|
||||
<li>
|
||||
<a id="set-group-weights" class="dialog_opener" role="button" aria-controls="assignment_group_weights_dialog" href="#">
|
||||
<%= t('set_group_weights', 'Set Group Weights') %>
|
||||
</a>
|
||||
</li>
|
||||
<% end %>
|
||||
<li>
|
||||
<a id="student_names_toggle" href="#"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a id="arrange_by_toggle" href="#"></a>
|
||||
</li>
|
||||
<li class="<% if @course_is_concluded %>ui-state-disabled<% end %>">
|
||||
<a>
|
||||
<label>
|
||||
<%= t('show_concluded_enrollments', "Show Concluded Enrollments") %>
|
||||
<input type="checkbox" id="show_concluded_enrollments" />
|
||||
</label>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a>
|
||||
<label>
|
||||
<%= t('show_inactive_enrollments', "Show Inactive Enrollments") %>
|
||||
<input type="checkbox" id="show_inactive_enrollments" />
|
||||
</label>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a id="notes_toggle" href="#"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div id="gradebook-grid-wrapper" class="use-css-transitions-for-show-hide">
|
||||
<div id="gradebook_grid"></div>
|
||||
</div>
|
||||
<div style="display:none;">
|
||||
<%= render :partial => "shared/message_students" %>
|
||||
<%= render :partial => 'submissions/submission_download' %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% if @context.feature_enabled?(:outcome_gradebook) %>
|
||||
<div class="outcome-gradebook-container hidden"></div>
|
||||
<% end %>
|
||||
</div>
|
|
@ -204,8 +204,6 @@ with the docker workflow
|
|||
(was a missing bundle, need to start sniffing for which bundles are actually in the
|
||||
directory soon)
|
||||
|
||||
[X] solve Module build failed: SyntaxError: /app/frontend_build/jsxYankPragma.js!/app/app/jsx/gradebook/grid/stores/tableStore.jsx: Argument name clash in strict mode (13:13)
|
||||
|
||||
[X] test in development and
|
||||
make sure we have source to
|
||||
debug with
|
||||
|
|
|
@ -229,10 +229,6 @@ module.exports = {
|
|||
new webpack.PrefetchPlugin("./app/jsx/files/ShowFolder.jsx"),
|
||||
new webpack.PrefetchPlugin("./app/jsx/files/UploadButton.jsx"),
|
||||
new webpack.PrefetchPlugin("./app/jsx/files/utils/openMoveDialog.jsx"),
|
||||
new webpack.PrefetchPlugin("./app/jsx/gradebook/grid/components/column_types/headerRenderer.jsx"),
|
||||
new webpack.PrefetchPlugin("./app/jsx/gradebook/grid/components/dropdown_components/assignmentHeaderDropdownOptions.jsx"),
|
||||
new webpack.PrefetchPlugin("./app/jsx/gradebook/grid/components/gradebook.jsx"),
|
||||
new webpack.PrefetchPlugin("./app/jsx/gradebook/grid/wrappers/columnFactory.jsx"),
|
||||
new webpack.PrefetchPlugin("./app/jsx/gradebook/SISGradePassback/PostGradesApp.jsx"),
|
||||
new webpack.PrefetchPlugin("./app/jsx/gradebook/SISGradePassback/PostGradesDialogCorrectionsPage.jsx"),
|
||||
new webpack.PrefetchPlugin("./app/jsx/grading/gradingPeriodCollection.jsx"),
|
||||
|
|
|
@ -88,7 +88,6 @@ module Canvas
|
|||
{name: 'redux-actions', location: 'symlink_to_node_modules/redux-actions', main: 'dist/redux-actions.min'},
|
||||
{name: 'redux-logger', location: 'symlink_to_node_modules/redux-logger', main: 'dist/index'},
|
||||
{name: 'redux-thunk', location: 'symlink_to_node_modules/redux-thunk', main: 'dist/redux-thunk'},
|
||||
{name: 'reflux', location: 'symlink_to_node_modules/reflux', main: 'dist/reflux'},
|
||||
{name: 'tinymce', location: 'symlink_to_node_modules/tinymce', main: 'tinymce'},
|
||||
{name: 'spin.js', location: 'symlink_to_node_modules/spin.js', main: 'spin' },
|
||||
].to_json
|
||||
|
@ -119,10 +118,6 @@ module Canvas
|
|||
deps: ['jquery', 'vendor/FileAPI/config'],
|
||||
exports: 'FileAPI'
|
||||
},
|
||||
'fixed-data-table': {
|
||||
deps: ['object_assign', 'react'],
|
||||
exports: 'fixed-data-table'
|
||||
},
|
||||
'fullcalendar/dist/lang-all': {
|
||||
deps: ['fullcalendar']
|
||||
},
|
||||
|
|
|
@ -373,14 +373,6 @@ END
|
|||
development: true,
|
||||
root_opt_in: false
|
||||
},
|
||||
'gradebook_performance' => {
|
||||
display_name: -> { I18n.t('Gradebook Performance') },
|
||||
description: -> { I18n.t('Performance enhancements for the Gradebook') },
|
||||
applies_to: 'Course',
|
||||
state: 'hidden',
|
||||
development: true,
|
||||
root_opt_in: true
|
||||
},
|
||||
'anonymous_grading' => {
|
||||
display_name: -> { I18n.t('Anonymous Grading') },
|
||||
description: -> { I18n.t("Anonymous grading forces student names to be hidden in SpeedGrader™") },
|
||||
|
|
|
@ -101,7 +101,6 @@
|
|||
"redux-actions": "0.11.0",
|
||||
"redux-logger": "2.6.1",
|
||||
"redux-thunk": "2.1.0",
|
||||
"reflux": "0.2.7",
|
||||
"spin.js": "2.3.2"
|
||||
},
|
||||
"repository": "instructure/canvas-lms",
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,53 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2015 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/>.
|
||||
*/
|
||||
|
||||
define([], function () {
|
||||
//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
|
||||
if (!Object.assign) {
|
||||
Object.defineProperty(Object, 'assign', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: function(target, firstSource) {
|
||||
'use strict';
|
||||
if (target === undefined || target === null) {
|
||||
throw new TypeError('Cannot convert first argument to object');
|
||||
}
|
||||
|
||||
var to = Object(target);
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
var nextSource = arguments[i];
|
||||
if (nextSource === undefined || nextSource === null) {
|
||||
continue;
|
||||
}
|
||||
nextSource = Object(nextSource);
|
||||
|
||||
var keysArray = Object.keys(Object(nextSource));
|
||||
for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
|
||||
var nextKey = keysArray[nextIndex];
|
||||
var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
|
||||
if (desc !== undefined && desc.enumerable) {
|
||||
to[nextKey] = nextSource[nextKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
return to;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
|
@ -1,6 +1,6 @@
|
|||
define [
|
||||
'compiled/gradebook2/GradebookHelpers'
|
||||
'jsx/gradebook/grid/constants'
|
||||
'jsx/gradebook/shared/constants'
|
||||
], (GradebookHelpers, GradebookConstants) ->
|
||||
module "GradebookHelpers#noErrorsOnPage",
|
||||
setup: ->
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
define [
|
||||
'compiled/gradebook2/gradeFormatter'
|
||||
], (GradeFormatter) ->
|
||||
module 'gradebook2.GradeFormatter',
|
||||
setup: () ->
|
||||
teardown: () ->
|
||||
|
||||
test 'returns "-" if score is null', () ->
|
||||
gradeFormatter = new GradeFormatter(null, 100)
|
||||
result = gradeFormatter.toString()
|
||||
equal(result, '-')
|
||||
|
||||
test 'returns "-" if possibleScore is 0 and score is 0', () -> # Evaluates to NaN
|
||||
gradeFormatter = new GradeFormatter(0, 0)
|
||||
result = gradeFormatter.toString()
|
||||
equal(result, '-')
|
||||
|
||||
test 'returns "-" if possibleScore is 0 and score is > 0', () -> # Evaluates to Infinity
|
||||
gradeFormatter = new GradeFormatter(5, 0)
|
||||
result = gradeFormatter.toString()
|
||||
equal(result, '-')
|
||||
|
||||
test 'returns "-" if possibleScore is null', () ->
|
||||
gradeFormatter = new GradeFormatter(5, null)
|
||||
result = gradeFormatter.toString()
|
||||
equal(result, '-')
|
||||
|
||||
test 'returns "-" if score / possibleScore is NaN', () ->
|
||||
gradeFormatter = new GradeFormatter(5, 'a')
|
||||
result = gradeFormatter.toString()
|
||||
equal(result, '-')
|
||||
|
||||
test 'returns score with a percent when valid', () ->
|
||||
gradeFormatter = new GradeFormatter(5, 5)
|
||||
result = gradeFormatter.toString()
|
||||
equal(result, '100%')
|
|
@ -1,111 +0,0 @@
|
|||
define [
|
||||
'react'
|
||||
'react-dom'
|
||||
'react-addons-test-utils'
|
||||
'jsx/gradebook/grid/components/assignmentGradeCell'
|
||||
'underscore'
|
||||
'jsx/gradebook/grid/constants'
|
||||
], (React, ReactDOM, {Simulate}, AssignmentGradeCell, _, GradebookConstants) ->
|
||||
|
||||
wrapper = document.getElementById('fixtures')
|
||||
|
||||
renderComponent = (props) ->
|
||||
element = React.createElement(AssignmentGradeCell, props)
|
||||
ReactDOM.render(element, wrapper)
|
||||
|
||||
buildComponent = (props, additionalProps) ->
|
||||
cellData = props || {
|
||||
columnData: {
|
||||
assignment: {id: '1', points_possible: 10}
|
||||
},
|
||||
renderer: mockComponent,
|
||||
activeCell: false,
|
||||
rowData: {}
|
||||
}
|
||||
$.extend(cellData, additionalProps)
|
||||
renderComponent(cellData)
|
||||
|
||||
mockComponent = React.createClass({
|
||||
render: () ->
|
||||
React.createElement('div')
|
||||
})
|
||||
|
||||
buildComponentWithSubmission = (additionalProps, submissionType) ->
|
||||
cellData =
|
||||
columnData:
|
||||
assignment: {id: '1', points_possible: 100}
|
||||
cellData:
|
||||
id: '1'
|
||||
submission_type: submissionType || 'discussion_topic'
|
||||
assignment_id: '1'
|
||||
workflow_state: 'submitted'
|
||||
renderer: mockComponent
|
||||
activeCell: false
|
||||
rowData: {}
|
||||
$.extend(cellData, additionalProps)
|
||||
buildComponent(cellData)
|
||||
|
||||
getIconClassName = (component) ->
|
||||
component.refs.icon.getDOMNode().className
|
||||
|
||||
module 'ReactGradebook.assignmentGradeCellComponent',
|
||||
teardown: ->
|
||||
ReactDOM.unmountComponentAtNode(wrapper)
|
||||
|
||||
test 'should mount', ->
|
||||
ok(buildComponentWithSubmission().isMounted())
|
||||
|
||||
module 'ReactGradebook.assignmentGradeCellComponent icons',
|
||||
setup: ->
|
||||
@component = buildComponentWithSubmission()
|
||||
teardown: ->
|
||||
ReactDOM.unmountComponentAtNode(wrapper)
|
||||
|
||||
test 'should render discussion icon when submission is not graded', ->
|
||||
expected = 'icon-discussion'
|
||||
actual = getIconClassName(@component)
|
||||
equal(actual, expected)
|
||||
|
||||
test 'should render online-url icon when submission is not graded', ->
|
||||
component = buildComponentWithSubmission({}, 'online_url')
|
||||
equal(getIconClassName(component), 'icon-link')
|
||||
|
||||
test 'should render text-url icon when submission is not graded', ->
|
||||
component = buildComponentWithSubmission({}, 'online_text_entry')
|
||||
equal(getIconClassName(component), 'icon-text')
|
||||
|
||||
test 'should render online-upload icon when submission is not graded', ->
|
||||
component = buildComponentWithSubmission({}, 'online_upload')
|
||||
equal(getIconClassName(component), 'icon-document')
|
||||
|
||||
test 'should render online-quiz icon when submission is not graded', ->
|
||||
component = buildComponentWithSubmission({}, 'online_quiz')
|
||||
equal(getIconClassName(component), 'icon-quiz')
|
||||
|
||||
test 'should render media_recording icon when submission is not graded', ->
|
||||
component = buildComponentWithSubmission({}, 'media_recording')
|
||||
equal(getIconClassName(component), 'icon-media')
|
||||
|
||||
test 'has "active" class when cell isActive', ->
|
||||
component = buildComponent(null, {activeCell: true})
|
||||
classList = component.refs.detailsDialog.props.className
|
||||
ok classList.indexOf('active') > -1
|
||||
|
||||
test 'does not have "active" class when cell is not active', ->
|
||||
component = buildComponent()
|
||||
classList = component.refs.detailsDialog.props.className
|
||||
ok (classList.indexOf('active') == -1)
|
||||
|
||||
test 'opens dialog when clicking the submissions details link', ->
|
||||
props =
|
||||
rowData:
|
||||
student:
|
||||
user:
|
||||
name: "Hello"
|
||||
id: "1"
|
||||
|
||||
component = buildComponent(null, props)
|
||||
openDialogStub = @stub(component, 'openDialog', (->))
|
||||
detailsDialogLink = component.refs.detailsDialog
|
||||
Simulate.click(detailsDialogLink)
|
||||
ok openDialogStub.calledOnce
|
|
@ -1,73 +0,0 @@
|
|||
define [
|
||||
'react'
|
||||
'react-dom'
|
||||
'jsx/gradebook/grid/components/column_types/assignmentGroupColumn'
|
||||
'jsx/gradebook/grid/stores/gradingPeriodsStore'
|
||||
'jsx/gradebook/grid/stores/assignmentGroupsStore'
|
||||
'jsx/gradebook/grid/constants'
|
||||
'helpers/fakeENV'
|
||||
'jquery'
|
||||
'jquery.ajaxJSON'
|
||||
], (React, ReactDOM, AssignmentGroupColumn, GradingPeriodsStore, AssignmentGroupsStore, GradebookConstants, fakeENV, $) ->
|
||||
|
||||
wrapper = document.getElementById('fixtures')
|
||||
|
||||
gradingPeriodsData = ->
|
||||
GRADEBOOK_OPTIONS:
|
||||
current_grading_period_id: '0'
|
||||
|
||||
assignmentGroups = ->
|
||||
[{ assignments: [{ id: '3', points_possible: 25 }]}]
|
||||
|
||||
submissions = ->
|
||||
[{ assignment_id: '3', id: '6', score: 25 }]
|
||||
|
||||
defaultProps = =>
|
||||
groups = assignmentGroups()
|
||||
cellData:
|
||||
submissions: submissions()
|
||||
assignmentGroup: groups[0],
|
||||
rowData:
|
||||
assignmentGroups: groups
|
||||
|
||||
buildComponent = (props) ->
|
||||
cellData = props || defaultProps()
|
||||
renderComponent(cellData)
|
||||
|
||||
renderComponent = (props) ->
|
||||
element = React.createElement(AssignmentGroupColumn, props)
|
||||
ReactDOM.render(element, wrapper)
|
||||
|
||||
innerHTML = (component) ->
|
||||
component.refs.cell.getDOMNode().innerHTML
|
||||
|
||||
module 'ReactGradebook.assignmentGroupColumn',
|
||||
setup: ->
|
||||
fakeENV.setup(gradingPeriodsData())
|
||||
GradebookConstants.refresh()
|
||||
GradingPeriodsStore.getInitialState()
|
||||
AssignmentGroupsStore.getInitialState()
|
||||
AssignmentGroupsStore.onLoadCompleted(assignmentGroups())
|
||||
teardown: ->
|
||||
ReactDOM.unmountComponentAtNode wrapper
|
||||
fakeENV.teardown()
|
||||
|
||||
test 'mounts on build', ->
|
||||
ok(buildComponent().isMounted())
|
||||
|
||||
test 'shows a "%" sign', ->
|
||||
component = buildComponent()
|
||||
ok(innerHTML(component).match(/%/))
|
||||
|
||||
test 'displays "-" if points possible is 0', ->
|
||||
props = defaultProps()
|
||||
props.cellData.assignmentGroups = [{assignments: []}]
|
||||
props.cellData.submissions = []
|
||||
|
||||
component = buildComponent(props)
|
||||
ok(innerHTML(component).match(/-/))
|
||||
|
||||
test 'has title attribute for assignment group cells', ->
|
||||
component = buildComponent(defaultProps())
|
||||
title = component.refs.cell.props.title
|
||||
equal("25 / 25", title)
|
|
@ -1,59 +0,0 @@
|
|||
define [
|
||||
'react'
|
||||
'react-dom'
|
||||
'jsx/gradebook/grid/components/column_types/assignmentLetterGrade'
|
||||
'jquery'
|
||||
], (React, ReactDOM, AssignmentLetterGrade, $) ->
|
||||
|
||||
wrapper = document.getElementById('fixtures')
|
||||
|
||||
renderComponent = (data) ->
|
||||
element = React.createElement(AssignmentLetterGrade, data)
|
||||
ReactDOM.render(element, wrapper)
|
||||
|
||||
buildComponent = (props, additionalProps) ->
|
||||
cellData = props || {cellData: {id: '1'}, rowData: {enrollment: {}, submissions: []}}
|
||||
$.extend(cellData, additionalProps)
|
||||
renderComponent(cellData)
|
||||
|
||||
buildComponentWithSubmission = (additionalProps, grade, gradingType) ->
|
||||
cellData =
|
||||
cellData: {id: '1', grading_type: gradingType || 'letter_grade'}
|
||||
submission: {id: '1', grade: grade, score: '10',assignment_id: '1'}
|
||||
rowData: {enrollment: {}}
|
||||
$.extend(cellData, additionalProps)
|
||||
buildComponent(cellData)
|
||||
|
||||
module 'ReactGradebook.assignmentLetterGradeComponent',
|
||||
teardown: ->
|
||||
ReactDOM.unmountComponentAtNode(wrapper)
|
||||
|
||||
test 'should mount', ->
|
||||
ok(buildComponent().isMounted())
|
||||
|
||||
test 'should display "-" if no submission is present', ->
|
||||
grade = buildComponent().refs.grade.getDOMNode().innerHTML
|
||||
equal(grade, '-')
|
||||
|
||||
test 'should display "-" if submission grade is null', ->
|
||||
component = buildComponentWithSubmission({}, null)
|
||||
grade = component.refs.grade.getDOMNode().innerHTML
|
||||
equal(grade, '-')
|
||||
|
||||
test 'should display grade and score when assignment is a letter grade', ->
|
||||
component = buildComponentWithSubmission({}, 'A')
|
||||
elements = component.refs.grade.getDOMNode().children
|
||||
grade = elements[0].innerHTML
|
||||
score = elements[1].innerHTML
|
||||
|
||||
equal(grade, 'A')
|
||||
equal(score, '10')
|
||||
|
||||
test 'should display only grade when assignment is a GPA scale grade', ->
|
||||
component = buildComponentWithSubmission({}, 'A', 'gpa_scale')
|
||||
elements = component.refs.grade.getDOMNode().children
|
||||
grade = elements[0].innerHTML
|
||||
score = elements[1].innerHTML
|
||||
|
||||
equal(grade, 'A')
|
||||
equal(score, '')
|
|
@ -1,84 +0,0 @@
|
|||
define [
|
||||
'react',
|
||||
'react-dom',
|
||||
'react-addons-test-utils',
|
||||
'jquery',
|
||||
'jsx/gradebook/grid/components/column_types/assignmentPassFail'
|
||||
], (React, ReactDOM, {Simulate}, $, AssignmentPassFail) ->
|
||||
|
||||
wrapper = document.getElementById('fixtures')
|
||||
|
||||
renderComponent = (submission) =>
|
||||
cellData = {id: 1}
|
||||
data =
|
||||
cellData: cellData
|
||||
submission: submission
|
||||
rowData: {enrollment: {}}
|
||||
isActiveCell: true
|
||||
element = React.createElement(AssignmentPassFail, data)
|
||||
ReactDOM.render(element, wrapper)
|
||||
|
||||
createSubmission = (score) =>
|
||||
id: 2
|
||||
assignment_id: 1
|
||||
workflow_state: 'graded'
|
||||
grade: score
|
||||
|
||||
createCompleteSubmission = () =>
|
||||
createSubmission('complete')
|
||||
|
||||
createIncompleteSubmission = () =>
|
||||
createSubmission('incomplete')
|
||||
|
||||
module 'ReactGradebook.assignmentPassFail',
|
||||
teardown: ->
|
||||
ReactDOM.unmountComponentAtNode(wrapper)
|
||||
|
||||
test 'should mount', =>
|
||||
ok(renderComponent().isMounted())
|
||||
|
||||
test 'should display "-" if no submission is present', =>
|
||||
grade = renderComponent().refs.grade.getDOMNode()
|
||||
grade = $(grade.innerHTML).text()
|
||||
equal(grade, '-')
|
||||
|
||||
test 'should display a checkbox when the submission is complete', =>
|
||||
submission = createCompleteSubmission()
|
||||
component = renderComponent(submission)
|
||||
element = component.refs.grade.getDOMNode()
|
||||
ok($(element).hasClass('gradebook-checkbox-complete'))
|
||||
|
||||
test 'should display an "x" for when the submission is incomplete', =>
|
||||
submission = createIncompleteSubmission()
|
||||
component = renderComponent(submission)
|
||||
element = component.refs.grade.getDOMNode()
|
||||
ok($(element).hasClass('gradebook-checkbox-incomplete'))
|
||||
|
||||
test 'should display an empty box after clicking a submission with a null grade', =>
|
||||
component = renderComponent()
|
||||
Simulate.click(component.refs.grade.getDOMNode())
|
||||
$element = $(component.refs.grade.getDOMNode())
|
||||
ok($element.hasClass('gradebook-checkbox'))
|
||||
ok($element.hasClass('gradebook-checkbox-null'))
|
||||
ok($element.hasClass('editable'))
|
||||
|
||||
test 'should display an editable checkbox after clicking a complete submission', =>
|
||||
component = renderComponent(createCompleteSubmission())
|
||||
Simulate.click(component.refs.grade.getDOMNode())
|
||||
$element = $(component.refs.grade.getDOMNode())
|
||||
ok($element.hasClass('gradebook-checkbox-complete'))
|
||||
ok($element.hasClass('editable'))
|
||||
|
||||
test 'should display an editable "x" after clicking an incomplete submission', =>
|
||||
component = renderComponent(createIncompleteSubmission())
|
||||
Simulate.click(component.refs.grade.getDOMNode())
|
||||
$element = $(component.refs.grade.getDOMNode())
|
||||
ok($element.hasClass('gradebook-checkbox-incomplete'))
|
||||
ok($element.hasClass('editable'))
|
||||
|
||||
test 'should change editable complete to editable incomplete submission', =>
|
||||
component = renderComponent(createCompleteSubmission())
|
||||
Simulate.click(component.refs.grade.getDOMNode())
|
||||
$element = $(component.refs.grade.getDOMNode())
|
||||
ok($element.hasClass('gradebook-checkbox-complete'))
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
define [
|
||||
'react'
|
||||
'react-dom'
|
||||
'jsx/gradebook/grid/components/column_types/assignmentPercentage'
|
||||
'jquery'
|
||||
'jquery.ajaxJSON'
|
||||
], (React, ReactDOM, AssignmentPercentage, $) ->
|
||||
|
||||
wrapper = document.getElementById('fixtures')
|
||||
|
||||
renderComponent = (data) ->
|
||||
element = React.createElement(AssignmentPercentage, data)
|
||||
ReactDOM.render(element, wrapper)
|
||||
|
||||
buildComponent = (props, additionalProps) ->
|
||||
cellData = props || {
|
||||
rowData: {
|
||||
student: {
|
||||
enrollment_state: "active"
|
||||
}
|
||||
}
|
||||
}
|
||||
$.extend(cellData, additionalProps)
|
||||
renderComponent(cellData)
|
||||
|
||||
buildComponentWithSubmission = (additionalProps) ->
|
||||
cellData =
|
||||
cellData: {id: '1', grade: '100%', assignment_id: '1'}
|
||||
rowData: {
|
||||
student: {
|
||||
enrollment_state: "active"
|
||||
}
|
||||
}
|
||||
$.extend(cellData, additionalProps)
|
||||
buildComponent(cellData)
|
||||
|
||||
module 'ReactGradebook.assignmentPercentageComponent',
|
||||
setup: ->
|
||||
teardown: ->
|
||||
ReactDOM.unmountComponentAtNode wrapper
|
||||
|
||||
test 'should mount', ->
|
||||
ok(buildComponent().isMounted())
|
||||
|
||||
test 'should display "-" if no submission present"', ->
|
||||
grade = buildComponent().refs.grade.getDOMNode().innerHTML
|
||||
equal(grade, '-')
|
||||
|
||||
test 'should display a grade when a submission is present', ->
|
||||
component = buildComponentWithSubmission()
|
||||
grade = component.refs.grade.getDOMNode().innerHTML
|
||||
equal(grade, '100')
|
||||
|
||||
test 'should add "%" to grade when editing', ->
|
||||
component = buildComponentWithSubmission({isActiveCell: true})
|
||||
grade = component.refs.gradeInput.getDOMNode().value
|
||||
equal(grade, '100%')
|
||||
|
||||
test 'should diplay a text field after clicking on the cell', ->
|
||||
component = buildComponent(null, {isActiveCell: true})
|
||||
equal(component.refs.gradeInput.getDOMNode().tagName, 'INPUT')
|
||||
|
||||
test 'should select the text of the grade after click', ->
|
||||
component = buildComponentWithSubmission({isActiveCell: true})
|
||||
component.componentDidUpdate({}, {})
|
||||
grade = component.refs.gradeInput.getDOMNode().value
|
||||
equal(grade, window.getSelection().toString())
|
||||
|
||||
#TODO: use squire
|
||||
#test 'should return to view state when enter is pressed when editing', ->
|
||||
# component = buildComponentWithSubmission()
|
||||
# Simulate.click(component.refs.grade.getDOMNode())
|
||||
# Simulate.keyUp(component.refs.gradeInput.getDOMNode(), {key: 'Enter'})
|
||||
# notOk(component.refs.gradeInput)
|
||||
# ok(component.refs.grade)
|
|
@ -1,60 +0,0 @@
|
|||
define [
|
||||
'react'
|
||||
'react-dom'
|
||||
'jsx/gradebook/grid/components/column_types/assignmentPoints'
|
||||
'jquery'
|
||||
], (React, ReactDOM, AssignmentPoints, $) ->
|
||||
|
||||
renderComponent = (data) ->
|
||||
element = React.createElement(AssignmentPoints, data)
|
||||
ReactDOM.render(element, wrapper)
|
||||
|
||||
buildComponent = (props, additionalProps) ->
|
||||
cellData = props || {
|
||||
columnData: { assignment: { id: '1', points_possible: 10 } }
|
||||
rowData: { student: { enrollment_state: 'active' } }
|
||||
}
|
||||
$.extend(cellData, additionalProps)
|
||||
renderComponent(cellData)
|
||||
|
||||
buildComponentWithSubmission = (additionalProps, grade) ->
|
||||
props =
|
||||
cellData: {id: '1', grade: grade, score: '10', assignment_id: '1'}
|
||||
columnData: {assignment: {id: '1', points_possible: 100}}
|
||||
rowData: { student: { enrollment_state: 'active' } }
|
||||
$.extend(props, additionalProps)
|
||||
buildComponent(props)
|
||||
|
||||
wrapper = document.getElementById('fixtures')
|
||||
|
||||
module 'ReactGradebook.assignmentPointsComponent',
|
||||
teardown: ->
|
||||
ReactDOM.unmountComponentAtNode(wrapper)
|
||||
|
||||
test 'should mount', ->
|
||||
ok(buildComponent().isMounted())
|
||||
|
||||
test 'should display "-" if no submission is present', ->
|
||||
grade = buildComponent().refs.grade.getDOMNode().innerHTML
|
||||
equal(grade, '-')
|
||||
|
||||
test 'should display "-" if the submission grade is null', ->
|
||||
component = buildComponentWithSubmission({}, null)
|
||||
grade = component.refs.grade.getDOMNode().innerHTML
|
||||
equal(grade, '-')
|
||||
|
||||
test 'should display "-/10" when first creating the submission', ->
|
||||
component = buildComponent(null, {isActiveCell: true})
|
||||
grade = component.refs.gradeInput.getDOMNode().value
|
||||
pointsPossible = component.refs.pointsPossible.getDOMNode().innerHTML
|
||||
|
||||
equal(grade, '-')
|
||||
equal(pointsPossible, '10')
|
||||
|
||||
test 'should display grade when submission has a grade', ->
|
||||
component = buildComponentWithSubmission({isActiveCell: true}, '10')
|
||||
grade = component.refs.gradeInput.getDOMNode().value
|
||||
pointsPossible = component.refs.pointsPossible.getDOMNode().innerHTML
|
||||
|
||||
equal(grade, '10')
|
||||
equal(pointsPossible, '100')
|
|
@ -1,124 +0,0 @@
|
|||
define [
|
||||
'react'
|
||||
'react-dom'
|
||||
'jsx/gradebook/grid/components/column_types/headerRenderer'
|
||||
'jquery'
|
||||
'jquery.instructure_date_and_time'
|
||||
'translations/_core_en'
|
||||
], (React, ReactDOM, HeaderRenderer, $) ->
|
||||
|
||||
wrapper = document.getElementById('fixtures')
|
||||
|
||||
displayedDueDate = (component) ->
|
||||
component.refs.dueDate.getDOMNode().textContent
|
||||
|
||||
defaultProps = () ->
|
||||
label: 'Column Label'
|
||||
columnData:
|
||||
assignment:
|
||||
due_at: '2015-07-17T05:59:59Z'
|
||||
enrollments: []
|
||||
submissions: {}
|
||||
|
||||
renderComponent = (data) ->
|
||||
element = React.createElement(HeaderRenderer, data)
|
||||
ReactDOM.render(element, wrapper)
|
||||
|
||||
buildComponent = (props) ->
|
||||
columnData = props
|
||||
renderComponent(columnData)
|
||||
|
||||
module 'HeaderRenderer',
|
||||
setup: ->
|
||||
teardown: ->
|
||||
ReactDOM.unmountComponentAtNode wrapper
|
||||
|
||||
test 'displays nothing if there is no assignment', ->
|
||||
props = defaultProps()
|
||||
props.columnData = {}
|
||||
|
||||
component = buildComponent(props)
|
||||
deepEqual displayedDueDate(component), ''
|
||||
|
||||
test 'displays "No due date" if the assignment has no due date and no overrides', ->
|
||||
props = defaultProps()
|
||||
props.columnData.assignment.due_at = null
|
||||
|
||||
component = buildComponent(props)
|
||||
deepEqual displayedDueDate(component), 'No due date'
|
||||
|
||||
test 'displays "No due date" if the assignment has no due date and one override with no due date', ->
|
||||
props = defaultProps()
|
||||
props.columnData.assignment.due_at = null
|
||||
props.columnData.assignment.overrides = [
|
||||
{ title: 'section 1', due_at: null }
|
||||
]
|
||||
|
||||
component = buildComponent(props)
|
||||
deepEqual displayedDueDate(component), 'No due date'
|
||||
|
||||
test 'displays the override due date if the assignment has no due date and one' +
|
||||
' override with a due date', ->
|
||||
@stub($, 'sameYear').returns(true)
|
||||
props = defaultProps()
|
||||
props.columnData.assignment.due_at = null
|
||||
props.columnData.assignment.overrides = [
|
||||
{ title: 'section 1', due_at: '2015-07-18T05:59:59Z' }
|
||||
]
|
||||
|
||||
component = buildComponent(props)
|
||||
deepEqual displayedDueDate(component), 'Due Jul 18'
|
||||
|
||||
test 'displays "Multiple due dates" if the assignment has more than one override', ->
|
||||
props = defaultProps()
|
||||
props.columnData.assignment.due_at = null
|
||||
props.columnData.assignment.overrides = [
|
||||
{ title: 'section 1', due_at: '2015-07-17T05:59:59Z' },
|
||||
{ title: 'section 2', due_at: '2015-07-18T05:59:59Z' }
|
||||
]
|
||||
|
||||
component = buildComponent(props)
|
||||
deepEqual displayedDueDate(component), 'Multiple due dates'
|
||||
|
||||
#this logic matches the logic in Assignment.coffee#multipleDueDates, which is
|
||||
#used to display due dates on the assignment page. I'm matching that logic
|
||||
#for the sake of consistency.
|
||||
test 'displays "Multiple due dates" if the assignment has more than one override,' +
|
||||
'even if those overrides do not have due dates themselves', ->
|
||||
props = defaultProps()
|
||||
props.columnData.assignment.due_at = null
|
||||
props.columnData.assignment.overrides = [
|
||||
{ title: 'section 1', due_at: null },
|
||||
{ title: 'section 2', due_at: null }
|
||||
]
|
||||
|
||||
component = buildComponent(props)
|
||||
deepEqual displayedDueDate(component), 'Multiple due dates'
|
||||
|
||||
test 'calculates the correct due date string for an assignment due in the current year', ->
|
||||
@stub($, 'sameYear').returns(true)
|
||||
props = defaultProps()
|
||||
|
||||
component = buildComponent(props)
|
||||
deepEqual component.headerDate(props.columnData), 'Due Jul 17'
|
||||
|
||||
test 'calculates the correct due date string for an assignment due in a different year', ->
|
||||
@stub($, 'sameYear').returns(false)
|
||||
props = defaultProps()
|
||||
|
||||
component = buildComponent(props)
|
||||
deepEqual component.headerDate(props.columnData), 'Due Jul 17, 2015'
|
||||
|
||||
test 'displays due date without the year if the assignment has a due date (current year)', ->
|
||||
@stub($, 'sameYear').returns(true)
|
||||
props = defaultProps()
|
||||
|
||||
component = buildComponent(props)
|
||||
deepEqual displayedDueDate(component), 'Due Jul 17'
|
||||
|
||||
test 'displays due date with the year if the assignment has a due date (different year)', ->
|
||||
@stub($, 'sameYear').returns(false)
|
||||
props = defaultProps()
|
||||
|
||||
component = buildComponent(props)
|
||||
deepEqual displayedDueDate(component), 'Due Jul 17, 2015'
|
|
@ -1,111 +0,0 @@
|
|||
define [
|
||||
'react'
|
||||
'react-dom'
|
||||
'react-addons-test-utils'
|
||||
'underscore'
|
||||
'jsx/gradebook/grid/components/column_types/teacherNote'
|
||||
'react-modal'
|
||||
'helpers/fakeENV'
|
||||
'jsx/gradebook/grid/constants'
|
||||
'compiled/gradebook2/GradebookHelpers'
|
||||
], (React, ReactDOM, {Simulate}, _, TeacherNote, Modal, fakeENV, GradebookConstants, GradebookHelpers) ->
|
||||
|
||||
wrapper = document.getElementById('fixtures')
|
||||
|
||||
renderComponent = ->
|
||||
props =
|
||||
note: 'Great work!'
|
||||
userId: '1'
|
||||
studentName: 'Dora Explora'
|
||||
columnId: '1'
|
||||
|
||||
componentFactory = React.createFactory(TeacherNote)
|
||||
ReactDOM.render(componentFactory(props), wrapper)
|
||||
|
||||
module 'ReactGradebook.teacherNoteComponent',
|
||||
setup: ->
|
||||
fakeENV.setup()
|
||||
ENV.GRADEBOOK_OPTIONS =
|
||||
teacher_notes: { id: '1' }
|
||||
custom_column_datum_url: 'http://fakeurl.com/api/v1/courses/1/custom_gradebook_columns/:id/data/:user_id'
|
||||
GradebookConstants.refresh()
|
||||
Modal.setAppElement(wrapper)
|
||||
teardown: ->
|
||||
ReactDOM.unmountComponentAtNode(wrapper)
|
||||
fakeENV.teardown()
|
||||
|
||||
test 'mounts', ->
|
||||
ok renderComponent().isMounted()
|
||||
|
||||
test 'clicking on the cell causes the modal to show up', ->
|
||||
component = renderComponent()
|
||||
notOk component.state.showModal
|
||||
Simulate.click(component.refs.noteCell.getDOMNode())
|
||||
|
||||
ok component.state.showModal
|
||||
|
||||
test 'hiding the modal sets the content back to the original note', ->
|
||||
component = renderComponent()
|
||||
component.setState(showModal: true, content: 'Some fancy new note!')
|
||||
deepEqual component.state.content, 'Some fancy new note!'
|
||||
component.hideModal()
|
||||
|
||||
deepEqual component.state.content, 'Great work!'
|
||||
|
||||
test 'the modal shows the student name if Show Student Names is selected', ->
|
||||
component = renderComponent()
|
||||
component.showModal()
|
||||
notOk component.state.toolbarOptions.hideStudentNames
|
||||
title = component.refs.studentName.props.children
|
||||
|
||||
deepEqual title, 'Notes for Dora Explora'
|
||||
|
||||
test 'the modal hides the student name if Hide Student Names is selected', ->
|
||||
component = renderComponent()
|
||||
component.setState(showModal: true, toolbarOptions: { hideStudentNames: true })
|
||||
ok component.state.toolbarOptions.hideStudentNames
|
||||
title = component.refs.studentName.props.children
|
||||
|
||||
deepEqual title, 'Notes for student (name hidden)'
|
||||
|
||||
test '#updateContent shows an error message if the new content greater than the max allowed length', ->
|
||||
component = renderComponent()
|
||||
flashError = @stub($, 'flashError')
|
||||
newNote = 'a'.repeat(GradebookConstants.MAX_NOTE_LENGTH + 1)
|
||||
fakeEvent = { target: { value: newNote } }
|
||||
component.updateContent(fakeEvent)
|
||||
|
||||
ok flashError.called
|
||||
deepEqual component.state.content, 'Great work!'
|
||||
|
||||
test '#updateContent does not show an error message if the new content is exactly the max allowed length', ->
|
||||
component = renderComponent()
|
||||
flashError = @stub($, 'flashError')
|
||||
newNote = 'a'.repeat(GradebookConstants.MAX_NOTE_LENGTH)
|
||||
fakeEvent = { target: { value: newNote } }
|
||||
component.updateContent(fakeEvent)
|
||||
|
||||
notOk flashError.called
|
||||
deepEqual component.state.content, newNote
|
||||
|
||||
test '#updateContent does not show an error message if the new content is too long, but an error is already showing', ->
|
||||
component = renderComponent()
|
||||
flashError = @stub($, 'flashError')
|
||||
@stub(GradebookHelpers, 'noErrorsOnPage', -> false)
|
||||
newNote = 'a'.repeat(GradebookConstants.MAX_NOTE_LENGTH + 1)
|
||||
fakeEvent = { target: { value: newNote } }
|
||||
component.updateContent(fakeEvent)
|
||||
|
||||
ok flashError.notCalled
|
||||
deepEqual component.state.content, 'Great work!'
|
||||
|
||||
test '#handleSubmit makes a PUT request with the new note data', ->
|
||||
component = renderComponent()
|
||||
newNote = 'Excellent new note.'
|
||||
component.setState(content: newNote)
|
||||
ajaxJSON = @stub($, 'ajaxJSON')
|
||||
component.handleSubmit()
|
||||
|
||||
deepEqual ajaxJSON.args[0][0], 'http://fakeurl.com/api/v1/courses/1/custom_gradebook_columns/1/data/1'
|
||||
deepEqual ajaxJSON.args[0][1], 'PUT'
|
||||
deepEqual ajaxJSON.args[0][2]['column_data[content]'], newNote
|
|
@ -1,194 +0,0 @@
|
|||
define [
|
||||
'underscore'
|
||||
'react'
|
||||
'react-dom'
|
||||
'jsx/gradebook/grid/components/column_types/totalColumn'
|
||||
'jsx/gradebook/grid/stores/gradingPeriodsStore'
|
||||
'jsx/gradebook/grid/stores/assignmentGroupsStore'
|
||||
'jsx/gradebook/grid/constants'
|
||||
'helpers/fakeENV'
|
||||
'jquery'
|
||||
'jquery.ajaxJSON'
|
||||
], (_, React, ReactDOM, TotalColumn, GradingPeriodsStore, AssignmentGroupsStore, GradebookConstants, fakeENV, $) ->
|
||||
|
||||
wrapper = document.getElementById('fixtures')
|
||||
|
||||
assignmentGroups = ->
|
||||
[{ assignments: [{ id: '3', points_possible: 25 }]}]
|
||||
|
||||
submissions = ->
|
||||
[{ assignment_id: '3', id: '6', score: 25 }]
|
||||
|
||||
propsWithSubmissions = ->
|
||||
rowData:
|
||||
assignmentGroups: [{ assignments: [{ id: '3', points_possible: 25 }]}],
|
||||
submissions: submissions()
|
||||
|
||||
gradingPeriodsData = ->
|
||||
GRADEBOOK_OPTIONS:
|
||||
current_grading_period_id: '0'
|
||||
|
||||
totalGradeOutput = (component) ->
|
||||
component.refs.totalGrade.getDOMNode().innerHTML
|
||||
|
||||
buildComponent = (props) ->
|
||||
cellData = props || {cellData: '1', rowData: {submissions: []}}
|
||||
element = React.createElement(TotalColumn, cellData)
|
||||
ReactDOM.render(element, wrapper)
|
||||
|
||||
firstTestAssignment = (opts) ->
|
||||
defaultAssignment = { name: 'assignment 1', id: '3', points_possible: 25, submission_types: ['graded'] }
|
||||
_.defaults(opts || {}, defaultAssignment)
|
||||
|
||||
secondTestAssignment = (opts) ->
|
||||
defaultAssignment = { name: 'assignment 2', id: '7', points_possible: 15, submission_types: ['graded'] }
|
||||
_.defaults(opts || {}, defaultAssignment)
|
||||
|
||||
generateCellData = (assignment1Properties, assignment2Properties, groupProperties) ->
|
||||
assignmentGroup = _.defaults(groupProperties || {}, { name: 'Group A' })
|
||||
assignmentGroup.assignments = [
|
||||
firstTestAssignment(assignment1Properties),
|
||||
secondTestAssignment(assignment2Properties)
|
||||
]
|
||||
rowData:
|
||||
assignmentGroups: [assignmentGroup]
|
||||
|
||||
module 'ReactGradebook.totalColumn',
|
||||
setup: ->
|
||||
fakeENV.setup(gradingPeriodsData())
|
||||
GradebookConstants.refresh()
|
||||
GradingPeriodsStore.getInitialState()
|
||||
AssignmentGroupsStore.getInitialState()
|
||||
AssignmentGroupsStore.onLoadCompleted(assignmentGroups())
|
||||
teardown: ->
|
||||
ReactDOM.unmountComponentAtNode wrapper
|
||||
AssignmentGroupsStore.assignmentGroups = undefined
|
||||
GradingPeriodsStore.gradingPeriods = undefined
|
||||
fakeENV.teardown()
|
||||
|
||||
test 'mounts on build', ->
|
||||
ok(buildComponent().isMounted())
|
||||
|
||||
test 'displays "-" if there are no submissions yet', ->
|
||||
component = buildComponent()
|
||||
deepEqual(totalGradeOutput(component), '-')
|
||||
|
||||
test 'Displays a % on a numeric value', ->
|
||||
props = propsWithSubmissions()
|
||||
component = buildComponent(props)
|
||||
deepEqual(totalGradeOutput(component), '100%')
|
||||
|
||||
test 'displays warning icon if all assignments combined have 0 points possible', ->
|
||||
cellData = generateCellData({ points_possible: 0 }, { points_possible: 0 }, { shouldShowNoPointsWarning: false })
|
||||
component = buildComponent(cellData)
|
||||
equal component.refs.icon.props.className, 'icon-warning final-warning'
|
||||
|
||||
test 'shows grade as points if the user selects "Show As Points"', ->
|
||||
props = propsWithSubmissions()
|
||||
component = buildComponent(props)
|
||||
component.setState({ toolbarOptions: { showTotalGradeAsPoints: true } })
|
||||
deepEqual(totalGradeOutput(component), '25')
|
||||
|
||||
test 'displays "-" if there are no submissions yet with "Show As Points" selected', ->
|
||||
component = buildComponent()
|
||||
component.setState({ toolbarOptions: { showTotalGradeAsPoints: true } })
|
||||
deepEqual(totalGradeOutput(component), '-')
|
||||
|
||||
test 'displays mute icon if an assignment is muted', ->
|
||||
cellData = generateCellData({ muted: true }, { muted: true })
|
||||
component = buildComponent(cellData)
|
||||
equal component.refs.icon.props.className, 'icon-muted final-warning'
|
||||
|
||||
test 'assignments(): picks all assignments out of assignmentGroups()', ->
|
||||
cellData = generateCellData()
|
||||
totalColumn = buildComponent(cellData)
|
||||
deepEqual(totalColumn.assignments(), [firstTestAssignment(), secondTestAssignment()])
|
||||
|
||||
test 'visibleAssignments(): assignments without the not_graded submissions type', ->
|
||||
cellData = generateCellData(submission_types: ['not_graded'])
|
||||
totalColumn = buildComponent(cellData)
|
||||
propEqual(totalColumn.visibleAssignments(), [secondTestAssignment()])
|
||||
|
||||
test 'getWarning() for anyMutedAssignments', ->
|
||||
cellData = generateCellData(muted: true)
|
||||
totalColumn = buildComponent(cellData)
|
||||
equal(totalColumn.getWarning(), "This grade differs from the student's view of the grade because some assignments are muted")
|
||||
|
||||
test 'getWarning() for group with no points', ->
|
||||
cellData = generateCellData({ submission_types: ['not_graded'] }, null, { shouldShowNoPointsWarning: true })
|
||||
groupWeightingSchemeData = ->
|
||||
GRADEBOOK_OPTIONS:
|
||||
group_weighting_scheme: 'percent'
|
||||
fakeENV.setup(groupWeightingSchemeData())
|
||||
totalColumn = buildComponent(cellData)
|
||||
equal(totalColumn.getWarning(), 'Score does not include Group A because it has no points possible')
|
||||
|
||||
test 'getWarning() for multiple groups with no points', ->
|
||||
cellData = generateCellData(
|
||||
{ submission_types: ['not_graded'], points_possible: null },
|
||||
{ points_possible: null },
|
||||
{ shouldShowNoPointsWarning: true }
|
||||
)
|
||||
groupB =
|
||||
shouldShowNoPointsWarning: true
|
||||
name: 'Group B'
|
||||
assignments: [
|
||||
{ id: '1', submission_types: ['not_graded']}
|
||||
{ id: '2', submission_types: ['graded']}
|
||||
]
|
||||
cellData.rowData.assignmentGroups.push(groupB)
|
||||
|
||||
groupWeightingSchemeData = ->
|
||||
GRADEBOOK_OPTIONS:
|
||||
group_weighting_scheme: 'percent'
|
||||
fakeENV.setup(groupWeightingSchemeData())
|
||||
totalColumn = buildComponent(cellData)
|
||||
equal(totalColumn.getWarning(), 'Score does not include Group A and Group B because they have no points possible')
|
||||
|
||||
|
||||
test 'getWarning() for noPointsPossible', ->
|
||||
cellData = generateCellData({ points_possible: 0 }, { points_possible: 0 })
|
||||
totalColumn = buildComponent(cellData)
|
||||
equal(totalColumn.getWarning(), "Can't compute score until an assignment has points possible")
|
||||
|
||||
test 'anyMutedAssignments() for one muted assignment', ->
|
||||
cellData = generateCellData(muted: true)
|
||||
totalColumn = buildComponent(cellData)
|
||||
ok(totalColumn.anyMutedAssignments())
|
||||
|
||||
test 'anyMutedAssignments() for no muted assignments', ->
|
||||
cellData = generateCellData()
|
||||
totalColumn = buildComponent(cellData)
|
||||
notOk(totalColumn.anyMutedAssignments())
|
||||
|
||||
test 'noPointsPossible() for null and 0 points is true', ->
|
||||
cellData = generateCellData({ points_possible: null }, { points_possible: 0 })
|
||||
totalColumn = buildComponent(cellData)
|
||||
ok(totalColumn.noPointsPossible())
|
||||
|
||||
test 'noPointsPossible() for greater than 0 points is false', ->
|
||||
cellData = generateCellData(points_possible: null)
|
||||
totalColumn = buildComponent(cellData)
|
||||
notOk(totalColumn.noPointsPossible())
|
||||
|
||||
test 'iconClassNames() produces no class names for no muted and has points', ->
|
||||
cellData = generateCellData()
|
||||
totalColumn = buildComponent(cellData)
|
||||
equal(totalColumn.iconClassNames(), '')
|
||||
|
||||
test 'iconClassNames() produces class names for muted', ->
|
||||
cellData = generateCellData(muted: true)
|
||||
totalColumn = buildComponent(cellData)
|
||||
equal(totalColumn.iconClassNames(), 'icon-muted final-warning')
|
||||
|
||||
test 'iconClassNames() produces class names no points', ->
|
||||
cellData = generateCellData({ points_possible: 0 }, { points_possible: 0 })
|
||||
totalColumn = buildComponent(cellData)
|
||||
equal(totalColumn.iconClassNames(), 'icon-warning final-warning')
|
||||
|
||||
test "assignmentGroups() returns rowData's assignmentGroups", ->
|
||||
cellData = generateCellData()
|
||||
totalColumn = buildComponent(cellData)
|
||||
propEqual(totalColumn.assignmentGroups(), [{
|
||||
name: 'Group A', assignments: [firstTestAssignment(), secondTestAssignment()]
|
||||
}] )
|
|
@ -1,74 +0,0 @@
|
|||
define [
|
||||
'react'
|
||||
'react-dom'
|
||||
'jsx/gradebook/grid/components/dropdown_components/assignmentHeaderDropdownOptions'
|
||||
'underscore'
|
||||
'jsx/gradebook/grid/constants'
|
||||
'helpers/fakeENV'
|
||||
], (React, ReactDOM, DropdownOptions, _, GradebookConstants, fakeENV) ->
|
||||
|
||||
wrapper = document.getElementById('fixtures')
|
||||
|
||||
generateProps = (props) ->
|
||||
defaultAssignmentAttributes =
|
||||
id: 1
|
||||
html_url: 'https://example.instructure.com/courses/1/assignments/1'
|
||||
submission_types: ['online_upload']
|
||||
has_submitted_submissions: true
|
||||
submissions_downloads: 1
|
||||
speedgrader_url: '/courses/1/gradebook/speed_grader?assignment_id=1'
|
||||
muted: false
|
||||
assignmentAttributes = _.defaults(props || {}, defaultAssignmentAttributes)
|
||||
return {
|
||||
assignment: assignmentAttributes,
|
||||
submissions: {},
|
||||
idAttribute: 'assignmentOptions',
|
||||
enrollments: []
|
||||
}
|
||||
|
||||
renderComponent = (data) ->
|
||||
element = React.createElement(DropdownOptions, data)
|
||||
ReactDOM.render(element, wrapper)
|
||||
|
||||
buildComponent = (props) ->
|
||||
renderComponent(generateProps(props))
|
||||
|
||||
module 'AssignmentHeaderDropdownOptions -- speedgrader enabled',
|
||||
setup: ->
|
||||
fakeENV.setup({ GRADEBOOK_OPTIONS: { context_id: '1', speed_grader_enabled: true, gradebook_is_editable: true } })
|
||||
GradebookConstants.refresh()
|
||||
teardown: ->
|
||||
ReactDOM.unmountComponentAtNode wrapper
|
||||
fakeENV.teardown()
|
||||
|
||||
test 'includes a "Download Submissions" option if there are downloadable submissions', ->
|
||||
component = buildComponent()
|
||||
ok component.refs.downloadSubmissions
|
||||
|
||||
test 'does not include a "Download Submissions" option if there no downloadable submisisons', ->
|
||||
component = buildComponent({ submisison_types: ['none'], has_submitted_submissions: false })
|
||||
notOk component.refs.downloadSubmissions
|
||||
|
||||
test 'includes a "Re-Upload Submissions" option if there is at least one submission download', ->
|
||||
component = buildComponent()
|
||||
ok component.refs.reuploadSubmissions
|
||||
|
||||
test 'does not include a "Re-Upload Submissions" option if there are no submission downloads', ->
|
||||
component = buildComponent({ submissions_downloads: 0 })
|
||||
notOk component.refs.reuploadSubmissions
|
||||
|
||||
test 'includes a "Speedgrader" option if speedgrader is enabled', ->
|
||||
component = buildComponent()
|
||||
ok component.refs.openSpeedgrader
|
||||
|
||||
module 'AssignmentHeaderDropdownOptions -- speedgrader disabled',
|
||||
setup: ->
|
||||
fakeENV.setup({ GRADEBOOK_OPTIONS: { context_id: '1', speed_grader_enabled: false } })
|
||||
GradebookConstants.refresh()
|
||||
teardown: ->
|
||||
ReactDOM.unmountComponentAtNode wrapper
|
||||
fakeENV.teardown()
|
||||
|
||||
test 'does not include a "Speedgrader" option if speedgrader is disabled', ->
|
||||
component = buildComponent()
|
||||
notOk component.refs.openSpeedgrader
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue