'
-
- submitCallback = jasmine.createSpy().and.returnValue()
- @autoenrollment.$student_enrollment_form.submit(submitCallback)
- @autoenrollment.$enrollment_signup_button.click()
- expect($('.results .list-summary').text()).toEqual('cannot read the line 2testuser1 (testemail1@email.com): (Username already exists)');
- expect(submitCallback).toHaveBeenCalled()
-
- it 'binds the ajax call and the result will be warnings', ->
- spyOn($, "ajax").and.callFake((params) =>
- params.success({
- row_errors: [],
- general_errors: [],
- warnings: [{
- 'username': 'user1',
- 'email': 'user1email',
- 'response': 'email is in valid'
- }]
- })
- {always: ->}
- )
- # mock the render_notification_view which returns the html (since we are only using the existing notification model)
- @autoenrollment.render_notification_view = jasmine.createSpy("render_notification_view(type, title, message, details) spy").and.callFake =>
- return '
Warnings
The following warnings were generated:
user1 (user1email): (email is in valid)
'
-
- submitCallback = jasmine.createSpy().and.returnValue()
- @autoenrollment.$student_enrollment_form.submit(submitCallback)
- @autoenrollment.$enrollment_signup_button.click()
- expect($('.results .list-summary').text()).toEqual('user1 (user1email): (email is in valid)')
- expect(submitCallback).toHaveBeenCalled()
\ No newline at end of file
diff --git a/lms/static/coffee/spec/instructor_dashboard/send_email_spec.coffee b/lms/static/coffee/spec/instructor_dashboard/send_email_spec.coffee
deleted file mode 100644
index 63250d12ae..0000000000
--- a/lms/static/coffee/spec/instructor_dashboard/send_email_spec.coffee
+++ /dev/null
@@ -1,92 +0,0 @@
-describe "Bulk Email Queueing", ->
- beforeEach ->
- testSubject = "Test Subject"
- testBody = "Hello, World! This is a test email message!"
- loadFixtures 'coffee/fixtures/send_email.html'
- @send_email = new SendEmail $('.send-email')
- @send_email.$subject.val(testSubject)
- @send_email.$send_to.first().prop("checked", true)
- @send_email.$emailEditor =
- save: ->
- {"data": testBody}
- @ajax_params = {
- type: "POST",
- dataType: "json",
- url: undefined,
- data: {
- action: "send",
- send_to: JSON.stringify([@send_email.$send_to.first().val()]),
- subject: testSubject,
- message: testBody,
- },
- success: jasmine.any(Function),
- error: jasmine.any(Function),
- }
-
- it 'cannot send an email with no target', ->
- spyOn(window, "alert")
- spyOn($, "ajax")
- for target in @send_email.$send_to
- target.checked = false
- @send_email.$btn_send.click()
- expect(window.alert).toHaveBeenCalledWith("Your message must have at least one target.")
- expect($.ajax).not.toHaveBeenCalled()
-
- it 'cannot send an email with no subject', ->
- spyOn(window, "alert")
- spyOn($, "ajax")
- @send_email.$subject.val("")
- @send_email.$btn_send.click()
- expect(window.alert).toHaveBeenCalledWith("Your message must have a subject.")
- expect($.ajax).not.toHaveBeenCalled()
-
- it 'cannot send an email with no message', ->
- spyOn(window, "alert")
- spyOn($, "ajax")
- @send_email.$emailEditor =
- save: ->
- {"data": ""}
- @send_email.$btn_send.click()
- expect(window.alert).toHaveBeenCalledWith("Your message cannot be blank.")
- expect($.ajax).not.toHaveBeenCalled()
-
- it 'can send a simple message to a single target', ->
- spyOn($, "ajax").and.callFake((params) =>
- params.success()
- )
- @send_email.$btn_send.click()
- expect($('.msg-confirm').text()).toEqual('Your email message was successfully queued for sending. In courses with a large number of learners, email messages to learners might take up to an hour to be sent.')
- expect($.ajax).toHaveBeenCalledWith(@ajax_params)
-
- it 'can send a simple message to a multiple targets', ->
- spyOn($, "ajax").and.callFake((params) =>
- params.success()
- )
- @ajax_params.data.send_to = JSON.stringify(target.value for target in @send_email.$send_to)
- for target in @send_email.$send_to
- target.checked = true
- @send_email.$btn_send.click()
- expect($('.msg-confirm').text()).toEqual('Your email message was successfully queued for sending. In courses with a large number of learners, email messages to learners might take up to an hour to be sent.')
- expect($.ajax).toHaveBeenCalledWith(@ajax_params)
-
- it 'can handle an error result from the bulk email api', ->
- spyOn($, "ajax").and.callFake((params) =>
- params.error()
- )
- spyOn(console, "warn")
- @send_email.$btn_send.click()
- expect($('.request-response-error').text()).toEqual('Error sending email.')
- expect(console.warn).toHaveBeenCalled()
-
- it 'selecting all learners disables cohort selections', ->
- @send_email.$send_to.filter("[value='learners']").click
- @send_email.$cohort_targets.each ->
- expect(this.disabled).toBe(true)
- @send_email.$send_to.filter("[value='learners']").click
- @send_email.$cohort_targets.each ->
- expect(this.disabled).toBe(false)
-
- it 'selected targets are listed after "send to:"', ->
- @send_email.$send_to.click
- $('input[name="send_to"]:checked+label').each ->
- expect($('.send_to_list'.text())).toContain(this.innerText.replace(/\s*\n.*/g,''))
diff --git a/lms/static/coffee/src/instructor_dashboard/course_info.coffee b/lms/static/coffee/src/instructor_dashboard/course_info.coffee
deleted file mode 100644
index c481a33bb5..0000000000
--- a/lms/static/coffee/src/instructor_dashboard/course_info.coffee
+++ /dev/null
@@ -1,55 +0,0 @@
-###
-Course Info Section
-
-imports from other modules.
-wrap in (-> ... apply) to defer evaluation
-such that the value can be defined later than this assignment (file load order).
-###
-
-# Load utilities
-PendingInstructorTasks = -> window.InstructorDashboard.util.PendingInstructorTasks
-
-# A typical section object.
-# constructed with $section, a jquery object
-# which holds the section body container.
-class CourseInfo
- constructor: (@$section) ->
- # attach self to html so that instructor_dashboard.coffee can find
- # this object to call event handlers like 'onClickTitle'
- @$section.data 'wrapper', @
-
- # gather elements
- @instructor_tasks = new (PendingInstructorTasks()) @$section
- @$course_errors_wrapper = @$section.find '.course-errors-wrapper'
-
- # if there are errors
- if @$course_errors_wrapper.length
- @$course_error_toggle = @$course_errors_wrapper.find '.toggle-wrapper'
- @$course_error_toggle_text = @$course_error_toggle.find 'h2'
- @$course_error_visibility_wrapper = @$course_errors_wrapper.find '.course-errors-visibility-wrapper'
- @$course_errors = @$course_errors_wrapper.find '.course-error'
-
- # append "(34)" to the course errors label
- @$course_error_toggle_text.text @$course_error_toggle_text.text() + " (#{@$course_errors.length})"
-
- # toggle .open class on errors
- # to show and hide them.
- @$course_error_toggle.click (e) =>
- e.preventDefault()
- if @$course_errors_wrapper.hasClass 'open'
- @$course_errors_wrapper.removeClass 'open'
- else
- @$course_errors_wrapper.addClass 'open'
-
- # handler for when the section title is clicked.
- onClickTitle: -> @instructor_tasks.task_poller.start()
-
- # handler for when the section is closed
- onExit: -> @instructor_tasks.task_poller.stop()
-
-# export for use
-# create parent namespaces if they do not already exist.
-_.defaults window, InstructorDashboard: {}
-_.defaults window.InstructorDashboard, sections: {}
-_.defaults window.InstructorDashboard.sections,
- CourseInfo: CourseInfo
diff --git a/lms/static/coffee/src/instructor_dashboard/data_download.coffee b/lms/static/coffee/src/instructor_dashboard/data_download.coffee
deleted file mode 100644
index 4fe762965a..0000000000
--- a/lms/static/coffee/src/instructor_dashboard/data_download.coffee
+++ /dev/null
@@ -1,297 +0,0 @@
-###
-Data Download Section
-
-imports from other modules.
-wrap in (-> ... apply) to defer evaluation
-such that the value can be defined later than this assignment (file load order).
-###
-
-# Load utilities
-std_ajax_err = -> window.InstructorDashboard.util.std_ajax_err.apply this, arguments
-PendingInstructorTasks = -> window.InstructorDashboard.util.PendingInstructorTasks
-ReportDownloads = -> window.InstructorDashboard.util.ReportDownloads
-
-# Data Download Certificate issued
-class @DataDownload_Certificate
- constructor: (@$container) ->
- # gather elements
- @$list_issued_certificate_table_btn = @$container.find("input[name='issued-certificates-list']")
- @$list_issued_certificate_csv_btn = @$container.find("input[name='issued-certificates-csv']")
- @$certificate_display_table = @$container.find '.certificate-data-display-table'
- @$certificates_request_response_error = @$container.find '.issued-certificates-error.request-response-error'
-
-
- @$list_issued_certificate_table_btn.click (e) =>
- url = @$list_issued_certificate_table_btn.data 'endpoint'
- # Dynamically generate slickgrid table for displaying issued certificate information.
- @clear_ui()
- @$certificate_display_table.text gettext('Loading data...')
- # fetch user list
- $.ajax
- type: 'POST'
- url: url
- error: (std_ajax_err) =>
- @clear_ui()
- @$certificates_request_response_error.text gettext("Error getting issued certificates list.")
- $(".issued_certificates .issued-certificates-error.msg-error").css({"display":"block"})
- success: (data) =>
- @clear_ui()
- # display on a SlickGrid
- options =
- enableCellNavigation: true
- enableColumnReorder: false
- forceFitColumns: true
- rowHeight: 35
-
- columns = ({id: feature, field: feature, name: data.feature_names[feature]} for feature in data.queried_features)
- grid_data = data.certificates
-
- $table_placeholder = $ '', class: 'slickgrid'
- @$certificate_display_table.append $table_placeholder
- new Slick.Grid($table_placeholder, grid_data, columns, options)
-
- @$list_issued_certificate_csv_btn.click (e) =>
- @clear_ui()
- url = @$list_issued_certificate_csv_btn.data 'endpoint'
- location.href = url + '?csv=true'
-
- clear_ui: ->
- # Clear any generated tables, warning messages, etc of certificates.
- @$certificate_display_table.empty()
- @$certificates_request_response_error.empty()
- $(".issued-certificates-error.msg-error").css({"display":"none"})
-
-# Data Download Section
-class DataDownload
- constructor: (@$section) ->
- # attach self to html so that instructor_dashboard.coffee can find
- # this object to call event handlers like 'onClickTitle'
- @$section.data 'wrapper', @
-
- # isolate # initialize DataDownload_Certificate subsection
- new DataDownload_Certificate @$section.find '.issued_certificates'
-
- # gather elements
- @$list_studs_btn = @$section.find("input[name='list-profiles']")
- @$list_studs_csv_btn = @$section.find("input[name='list-profiles-csv']")
- @$list_proctored_exam_results_csv_btn = @$section.find("input[name='proctored-exam-results-report']")
- @$survey_results_csv_btn = @$section.find("input[name='survey-results-report']")
- @$list_may_enroll_csv_btn = @$section.find("input[name='list-may-enroll-csv']")
- @$list_problem_responses_csv_input = @$section.find("input[name='problem-location']")
- @$list_problem_responses_csv_btn = @$section.find("input[name='list-problem-responses-csv']")
- @$list_anon_btn = @$section.find("input[name='list-anon-ids']")
- @$grade_config_btn = @$section.find("input[name='dump-gradeconf']")
- @$calculate_grades_csv_btn = @$section.find("input[name='calculate-grades-csv']")
- @$problem_grade_report_csv_btn = @$section.find("input[name='problem-grade-report']")
- @$async_report_btn = @$section.find("input[class='async-report-btn']")
-
- # response areas
- @$download = @$section.find '.data-download-container'
- @$download_display_text = @$download.find '.data-display-text'
- @$download_request_response_error = @$download.find '.request-response-error'
- @$reports = @$section.find '.reports-download-container'
- @$download_display_table = @$reports.find '.profile-data-display-table'
- @$reports_request_response = @$reports.find '.request-response'
- @$reports_request_response_error = @$reports.find '.request-response-error'
-
- @report_downloads = new (ReportDownloads()) @$section
- @instructor_tasks = new (PendingInstructorTasks()) @$section
- @clear_display()
-
- # attach click handlers
- # The list-anon case is always CSV
- @$list_anon_btn.click (e) =>
- url = @$list_anon_btn.data 'endpoint'
- location.href = url
-
- # attach click handlers
- # The list_proctored_exam_results case is always CSV
- @$list_proctored_exam_results_csv_btn.click (e) =>
- url = @$list_proctored_exam_results_csv_btn.data 'endpoint'
- # display html from proctored exam results config endpoint
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: url
- error: (std_ajax_err) =>
- @clear_display()
- @$reports_request_response_error.text gettext(
- "Error generating proctored exam results. Please try again."
- )
- $(".msg-error").css({"display":"block"})
- success: (data) =>
- @clear_display()
- @$reports_request_response.text data['status']
- $(".msg-confirm").css({"display":"block"})
-
- # attach click handlers
- # The list_proctored_exam_results case is always CSV
- @$survey_results_csv_btn.click (e) =>
- url = @$survey_results_csv_btn.data 'endpoint'
- # display html from survey results config endpoint
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: url
- error: (std_ajax_err) =>
- @clear_display()
- @$reports_request_response_error.text gettext(
- "Error generating survey results. Please try again."
- )
- $(".msg-error").css({"display":"block"})
- success: (data) =>
- @clear_display()
- @$reports_request_response.text data['status']
- $(".msg-confirm").css({"display":"block"})
-
- # this handler binds to both the download
- # and the csv button
- @$list_studs_csv_btn.click (e) =>
- @clear_display()
-
- url = @$list_studs_csv_btn.data 'endpoint'
- # handle csv special case
- # redirect the document to the csv file.
- url += '/csv'
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: url
- error: (std_ajax_err) =>
- @$reports_request_response_error.text gettext("Error generating student profile information. Please try again.")
- $(".msg-error").css({"display":"block"})
- success: (data) =>
- @$reports_request_response.text data['status']
- $(".msg-confirm").css({"display":"block"})
-
- @$list_studs_btn.click (e) =>
- url = @$list_studs_btn.data 'endpoint'
-
- # Dynamically generate slickgrid table for displaying student profile information
- @clear_display()
- @$download_display_table.text gettext('Loading')
-
- # fetch user list
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: url
- error: (std_ajax_err) =>
- @clear_display()
- @$download_request_response_error.text gettext("Error getting student list.")
- success: (data) =>
- @clear_display()
-
- # display on a SlickGrid
- options =
- enableCellNavigation: true
- enableColumnReorder: false
- forceFitColumns: true
- rowHeight: 35
-
- columns = ({id: feature, field: feature, name: data.feature_names[feature]} for feature in data.queried_features)
- grid_data = data.students
-
- $table_placeholder = $ '', class: 'slickgrid'
- @$download_display_table.append $table_placeholder
- grid = new Slick.Grid($table_placeholder, grid_data, columns, options)
- # grid.autosizeColumns()
-
- @$list_problem_responses_csv_btn.click (e) =>
- @clear_display()
-
- url = @$list_problem_responses_csv_btn.data 'endpoint'
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: url
- data:
- problem_location: @$list_problem_responses_csv_input.val()
- error: (std_ajax_err) =>
- @$reports_request_response_error.text JSON.parse(std_ajax_err['responseText'])
- $(".msg-error").css({"display":"block"})
- success: (data) =>
- @$reports_request_response.text data['status']
- $(".msg-confirm").css({"display":"block"})
-
- @$list_may_enroll_csv_btn.click (e) =>
- @clear_display()
-
- url = @$list_may_enroll_csv_btn.data 'endpoint'
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: url
- error: (std_ajax_err) =>
- @$reports_request_response_error.text gettext("Error generating list of students who may enroll. Please try again.")
- $(".msg-error").css({"display":"block"})
- success: (data) =>
- @$reports_request_response.text data['status']
- $(".msg-confirm").css({"display":"block"})
-
- @$grade_config_btn.click (e) =>
- url = @$grade_config_btn.data 'endpoint'
- # display html from grading config endpoint
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: url
- error: (std_ajax_err) =>
- @clear_display()
- @$download_request_response_error.text gettext("Error retrieving grading configuration.")
- success: (data) =>
- @clear_display()
- @$download_display_text.html data['grading_config_summary']
-
- @$async_report_btn.click (e) =>
- # Clear any CSS styling from the request-response areas
- #$(".msg-confirm").css({"display":"none"})
- #$(".msg-error").css({"display":"none"})
- @clear_display()
- url = $(e.target).data 'endpoint'
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: url
- error: std_ajax_err =>
- if e.target.name == 'calculate-grades-csv'
- @$grades_request_response_error.text gettext("Error generating grades. Please try again.")
- else if e.target.name == 'problem-grade-report'
- @$grades_request_response_error.text gettext("Error generating problem grade report. Please try again.")
- else if e.target.name == 'export-ora2-data'
- @$grades_request_response_error.text gettext("Error generating ORA data report. Please try again.")
- $(".msg-error").css({"display":"block"})
- success: (data) =>
- @$reports_request_response.text data['status']
- $(".msg-confirm").css({"display":"block"})
-
- # handler for when the section title is clicked.
- onClickTitle: ->
- # Clear display of anything that was here before
- @clear_display()
- @instructor_tasks.task_poller.start()
- @report_downloads.downloads_poller.start()
-
- # handler for when the section is closed
- onExit: ->
- @instructor_tasks.task_poller.stop()
- @report_downloads.downloads_poller.stop()
-
- clear_display: ->
- # Clear any generated tables, warning messages, etc.
- @$download_display_text.empty()
- @$download_display_table.empty()
- @$download_request_response_error.empty()
- @$reports_request_response.empty()
- @$reports_request_response_error.empty()
- # Clear any CSS styling from the request-response areas
- $(".msg-confirm").css({"display":"none"})
- $(".msg-error").css({"display":"none"})
-
-# export for use
-# create parent namespaces if they do not already exist.
-_.defaults window, InstructorDashboard: {}
-_.defaults window.InstructorDashboard, sections: {}
-_.defaults window.InstructorDashboard.sections,
- DataDownload: DataDownload
diff --git a/lms/static/coffee/src/instructor_dashboard/e-commerce.coffee b/lms/static/coffee/src/instructor_dashboard/e-commerce.coffee
deleted file mode 100644
index acc761c887..0000000000
--- a/lms/static/coffee/src/instructor_dashboard/e-commerce.coffee
+++ /dev/null
@@ -1,92 +0,0 @@
-###
-E-Commerce Section
-###
-
-# Load utilities
-PendingInstructorTasks = -> window.InstructorDashboard.util.PendingInstructorTasks
-ReportDownloads = -> window.InstructorDashboard.util.ReportDownloads
-
-class ECommerce
-# E-Commerce Section
- constructor: (@$section) ->
- # attach self to html so that instructor_dashboard.coffee can find
- # this object to call event handlers like 'onClickTitle'
- @$section.data 'wrapper', @
- # gather elements
- @$list_sale_csv_btn = @$section.find("input[name='list-sale-csv']")
- @$list_order_sale_csv_btn = @$section.find("input[name='list-order-sale-csv']")
- @$download_company_name = @$section.find("input[name='download_company_name']")
- @$active_company_name = @$section.find("input[name='active_company_name']")
- @$spent_company_name = @$section.find('input[name="spent_company_name"]')
- @$download_coupon_codes = @$section.find('input[name="download-coupon-codes-csv"]')
-
- @$download_registration_codes_form = @$section.find("form#download_registration_codes")
- @$active_registration_codes_form = @$section.find("form#active_registration_codes")
- @$spent_registration_codes_form = @$section.find("form#spent_registration_codes")
-
- @$reports = @$section.find '.reports-download-container'
- @$reports_request_response = @$reports.find '.request-response'
- @$reports_request_response_error = @$reports.find '.request-response-error'
-
- @report_downloads = new (ReportDownloads()) @$section
- @instructor_tasks = new (PendingInstructorTasks()) @$section
-
- @$error_msg = @$section.find('#error-msg')
-
- # attach click handlers
- # this handler binds to both the download
- # and the csv button
- @$list_sale_csv_btn.click (e) =>
- url = @$list_sale_csv_btn.data 'endpoint'
- url += '/csv'
- location.href = url
-
- @$list_order_sale_csv_btn.click (e) =>
- url = @$list_order_sale_csv_btn.data 'endpoint'
- location.href = url
-
- @$download_coupon_codes.click (e) =>
- url = @$download_coupon_codes.data 'endpoint'
- location.href = url
-
- @$download_registration_codes_form.submit (e) =>
- @$error_msg.attr('style', 'display: none')
- return true
-
- @$active_registration_codes_form.submit (e) =>
- @$error_msg.attr('style', 'display: none')
- return true
-
- @$spent_registration_codes_form.submit (e) =>
- @$error_msg.attr('style', 'display: none')
- return true
-
- # handler for when the section title is clicked.
- onClickTitle: ->
- @clear_display()
- @instructor_tasks.task_poller.start()
- @report_downloads.downloads_poller.start()
-
- # handler for when the section is closed
- onExit: ->
- @clear_display()
- @instructor_tasks.task_poller.stop()
- @report_downloads.downloads_poller.stop()
-
- clear_display: ->
- @$error_msg.attr('style', 'display: none')
- @$download_company_name.val('')
- @$reports_request_response.empty()
- @$reports_request_response_error.empty()
- @$active_company_name.val('')
- @$spent_company_name.val('')
-
- isInt = (n) -> return n % 1 == 0;
- # Clear any generated tables, warning messages, etc.
-
-# export for use
-# create parent namespaces if they do not already exist.
-_.defaults window, InstructorDashboard: {}
-_.defaults window.InstructorDashboard, sections: {}
-_.defaults window.InstructorDashboard.sections,
- ECommerce: ECommerce
diff --git a/lms/static/coffee/src/instructor_dashboard/extensions.coffee b/lms/static/coffee/src/instructor_dashboard/extensions.coffee
deleted file mode 100644
index a45cffce67..0000000000
--- a/lms/static/coffee/src/instructor_dashboard/extensions.coffee
+++ /dev/null
@@ -1,155 +0,0 @@
-###
-Extensions Section
-
-imports from other modules.
-wrap in (-> ... apply) to defer evaluation
-such that the value can be defined later than this assignment (file load order).
-###
-
-plantTimeout = -> window.InstructorDashboard.util.plantTimeout.apply this, arguments
-std_ajax_err = -> window.InstructorDashboard.util.std_ajax_err.apply this, arguments
-
-# Extensions Section
-class Extensions
-
- constructor: (@$section) ->
- # attach self to html
- # so that instructor_dashboard.coffee can find this object
- # to call event handlers like 'onClickTitle'
- @$section.data 'wrapper', @
-
- # Gather buttons
- @$change_due_date = @$section.find("input[name='change-due-date']")
- @$reset_due_date = @$section.find("input[name='reset-due-date']")
- @$show_unit_extensions = @$section.find("input[name='show-unit-extensions']")
- @$show_student_extensions = @$section.find("input[name='show-student-extensions']")
-
- # Gather notification areas
- @$section.find(".request-response").hide()
- @$section.find(".request-response-error").hide()
-
- # Gather grid elements
- $grid_display = @$section.find '.data-display'
- @$grid_text = $grid_display.find '.data-display-text'
- @$grid_table = $grid_display.find '.data-display-table'
-
- # Click handlers
- @$change_due_date.click =>
- @clear_display()
- @$student_input = @$section.find("#set-extension input[name='student']")
- @$url_input = @$section.find("#set-extension select[name='url']")
- @$due_datetime_input = @$section.find("#set-extension input[name='due_datetime']")
- send_data =
- student: @$student_input.val()
- url: @$url_input.val()
- due_datetime: @$due_datetime_input.val()
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$change_due_date.data 'endpoint'
- data: send_data
- success: (data) => @display_response "set-extension", data
- error: (xhr) => @fail_with_error "set-extension", "Error changing due date", xhr
-
- @$reset_due_date.click =>
- @clear_display()
- @$student_input = @$section.find("#reset-extension input[name='student']")
- @$url_input = @$section.find("#reset-extension select[name='url']")
- send_data =
- student: @$student_input.val()
- url: @$url_input.val()
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$reset_due_date.data 'endpoint'
- data: send_data
- success: (data) => @display_response "reset-extension", data
- error: (xhr) => @fail_with_error "reset-extension", "Error reseting due date", xhr
-
- @$show_unit_extensions.click =>
- @clear_display()
- @$grid_table.text 'Loading'
-
- @$url_input = @$section.find("#view-granted-extensions select[name='url']")
- url = @$show_unit_extensions.data 'endpoint'
- send_data =
- url: @$url_input.val()
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: url
- data: send_data
- error: (xhr) => @fail_with_error "view-granted-extensions", "Error getting due dates", xhr
- success: (data) => @display_grid data
-
- @$show_student_extensions.click =>
- @clear_display()
- @$grid_table.text 'Loading'
-
- url = @$show_student_extensions.data 'endpoint'
- @$student_input = @$section.find("#view-granted-extensions input[name='student']")
- send_data =
- student: @$student_input.val()
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: url
- data: send_data
- error: (xhr) => @fail_with_error "view-granted-extensions", "Error getting due dates", xhr
- success: (data) => @display_grid data
-
- # handler for when the section title is clicked.
- onClickTitle: ->
-
- fail_with_error: (id, msg, xhr) ->
- $task_error = @$section.find("#" + id + " .request-response-error")
- $task_response = @$section.find("#" + id + " .request-response")
- @clear_display()
- data = $.parseJSON xhr.responseText
- msg += ": " + data['error']
- console.warn msg
- $task_response.empty()
- $task_error.empty()
- $task_error.text msg
- $task_error.show()
-
- display_response: (id, data) ->
- $task_error = @$section.find("#" + id + " .request-response-error")
- $task_response = @$section.find("#" + id + " .request-response")
- $task_error.empty().hide()
- $task_response.empty().text data
- $task_response.show()
-
- display_grid: (data) ->
- @clear_display()
- @$grid_text.text data.title
-
- # display on a SlickGrid
- options =
- enableCellNavigation: true
- enableColumnReorder: false
- forceFitColumns: true
-
- columns = ({id: col, field: col, name: col} for col in data.header)
- grid_data = data.data
-
- $table_placeholder = $ '', class: 'slickgrid', style: 'min-height: 400px'
- @$grid_table.append $table_placeholder
- grid = new Slick.Grid($table_placeholder, grid_data, columns, options)
-
- clear_display: ->
- @$grid_text.empty()
- @$grid_table.empty()
- @$section.find(".request-response-error").empty().hide()
- @$section.find(".request-response").empty().hide()
-
-# export for use
-# create parent namespaces if they do not already exist.
-# abort if underscore can not be found.
-if _?
- _.defaults window, InstructorDashboard: {}
- _.defaults window.InstructorDashboard, sections: {}
- _.defaults window.InstructorDashboard.sections,
- Extensions: Extensions
diff --git a/lms/static/coffee/src/instructor_dashboard/instructor_dashboard.coffee b/lms/static/coffee/src/instructor_dashboard/instructor_dashboard.coffee
deleted file mode 100644
index 7beee97056..0000000000
--- a/lms/static/coffee/src/instructor_dashboard/instructor_dashboard.coffee
+++ /dev/null
@@ -1,202 +0,0 @@
-###
-Instructor Dashboard Tab Manager
-
-The instructor dashboard is broken into sections.
-
-Only one section is visible at a time,
- and is responsible for its own functionality.
-
-NOTE: plantTimeout (which is just setTimeout from util.coffee)
- is used frequently in the instructor dashboard to isolate
- failures. If one piece of code under a plantTimeout fails
- then it will not crash the rest of the dashboard.
-
-NOTE: The instructor dashboard currently does not
- use backbone. Just lots of jquery. This should be fixed.
-
-NOTE: Server endpoints in the dashboard are stored in
- the 'data-endpoint' attribute of relevant html elements.
- The urls are rendered there by a template.
-
-NOTE: For an example of what a section object should look like
- see course_info.coffee
-
-imports from other modules
-wrap in (-> ... apply) to defer evaluation
-such that the value can be defined later than this assignment (file load order).
-###
-
-plantTimeout = -> window.InstructorDashboard.util.plantTimeout.apply this, arguments
-std_ajax_err = -> window.InstructorDashboard.util.std_ajax_err.apply this, arguments
-
-# CSS classes
-CSS_INSTRUCTOR_CONTENT = 'instructor-dashboard-content-2'
-CSS_ACTIVE_SECTION = 'active-section'
-CSS_IDASH_SECTION = 'idash-section'
-CSS_INSTRUCTOR_NAV = 'instructor-nav'
-
-# prefix for deep-linking
-HASH_LINK_PREFIX = '#view-'
-
-$active_section = null
-
-# helper class for queueing and fault isolation.
-# Will execute functions marked by waiter.after only after all functions marked by
-# waiter.waitFor have been called.
-# To guarantee this functionality, waitFor and after must be called
-# before the functions passed to waitFor are called.
-class SafeWaiter
- constructor: ->
- @after_handlers = []
- @waitFor_handlers = []
- @fired = false
-
- after: (f) ->
- if @fired
- f()
- else
- @after_handlers.push f
-
- waitFor: (f) ->
- return if @fired
- @waitFor_handlers.push f
-
- # wrap the function so that it notifies the waiter
- # and can fire the after handlers.
- =>
- @waitFor_handlers = @waitFor_handlers.filter (g) -> g isnt f
- if @waitFor_handlers.length is 0
- @fired = true
- @after_handlers.map (cb) -> plantTimeout 0, cb
-
- f.apply this, arguments
-
-
-# waiter for dashboard sections.
-# Will only execute after all sections have at least attempted to load.
-# This is here to facilitate section constructors isolated by setTimeout
-# while still being able to interact with them under the guarantee
-# that the sections will be initialized at call time.
-sections_have_loaded = new SafeWaiter
-
-# once we're ready, check if this page is the instructor dashboard
-$ =>
- instructor_dashboard_content = $ ".#{CSS_INSTRUCTOR_CONTENT}"
- if instructor_dashboard_content.length > 0
- setup_instructor_dashboard instructor_dashboard_content
- setup_instructor_dashboard_sections instructor_dashboard_content
-
-
-# enable navigation bar
-# handles hiding and showing sections
-setup_instructor_dashboard = (idash_content) =>
- # clickable section titles
- $links = idash_content.find(".#{CSS_INSTRUCTOR_NAV}").find('.btn-link')
-
- # attach link click handlers
- $links.each (i, link) ->
- $(link).click (e) ->
- e.preventDefault()
-
- # deactivate all link & section styles
- idash_content.find(".#{CSS_INSTRUCTOR_NAV} li").children().removeClass CSS_ACTIVE_SECTION
- idash_content.find(".#{CSS_INSTRUCTOR_NAV} li").children().attr('aria-pressed', 'false')
- idash_content.find(".#{CSS_IDASH_SECTION}").removeClass CSS_ACTIVE_SECTION
-
- # discover section paired to link
- section_name = $(this).data 'section'
- $section = idash_content.find "##{section_name}"
-
- # activate link & section styling
- $(this).addClass CSS_ACTIVE_SECTION
- $(this).attr('aria-pressed','true')
- $section.addClass CSS_ACTIVE_SECTION
-
- # tracking
- analytics.pageview "instructor_section:#{section_name}"
-
- # deep linking
- # write to url
- location.hash = "#{HASH_LINK_PREFIX}#{section_name}"
-
- sections_have_loaded.after ->
- $section.data('wrapper').onClickTitle()
-
- # call onExit handler if exiting a section to a different section.
- unless $section.is $active_section
- $active_section?.data('wrapper')?.onExit?()
- $active_section = $section
-
- # TODO enable onExit handler
-
-
- # activate an initial section by 'clicking' on it.
- # check for a deep-link, or click the first link.
- click_first_link = ->
- link = $links.eq(0)
- link.click()
-
- if (new RegExp "^#{HASH_LINK_PREFIX}").test location.hash
- rmatch = (new RegExp "^#{HASH_LINK_PREFIX}(.*)").exec location.hash
- section_name = rmatch[1]
- link = $links.filter "[data-section='#{section_name}']"
- if link.length == 1
- link.click()
- else
- click_first_link()
- else
- click_first_link()
-
-
-
-# enable sections
-setup_instructor_dashboard_sections = (idash_content) ->
- sections_to_initialize = [
- constructor: window.InstructorDashboard.sections.CourseInfo
- $element: idash_content.find ".#{CSS_IDASH_SECTION}#course_info"
- ,
- constructor: window.InstructorDashboard.sections.DataDownload
- $element: idash_content.find ".#{CSS_IDASH_SECTION}#data_download"
- ,
- constructor: window.InstructorDashboard.sections.ECommerce
- $element: idash_content.find ".#{CSS_IDASH_SECTION}#e-commerce"
- ,
- constructor: window.InstructorDashboard.sections.Membership
- $element: idash_content.find ".#{CSS_IDASH_SECTION}#membership"
- ,
- constructor: window.InstructorDashboard.sections.StudentAdmin
- $element: idash_content.find ".#{CSS_IDASH_SECTION}#student_admin"
- ,
- constructor: window.InstructorDashboard.sections.Extensions
- $element: idash_content.find ".#{CSS_IDASH_SECTION}#extensions"
- ,
- constructor: window.InstructorDashboard.sections.Email
- $element: idash_content.find ".#{CSS_IDASH_SECTION}#send_email"
- ,
- constructor: window.InstructorDashboard.sections.InstructorAnalytics
- $element: idash_content.find ".#{CSS_IDASH_SECTION}#instructor_analytics"
- ,
- constructor: window.InstructorDashboard.sections.Metrics
- $element: idash_content.find ".#{CSS_IDASH_SECTION}#metrics"
- ,
- constructor: window.InstructorDashboard.sections.CohortManagement
- $element: idash_content.find ".#{CSS_IDASH_SECTION}#cohort_management"
- ,
- constructor: window.InstructorDashboard.sections.Certificates
- $element: idash_content.find ".#{CSS_IDASH_SECTION}#certificates"
- ]
- # proctoring can be feature disabled
- if edx.instructor_dashboard.proctoring != undefined
- sections_to_initialize = sections_to_initialize.concat [
- constructor: edx.instructor_dashboard.proctoring.ProctoredExamAllowanceView
- $element: idash_content.find ".#{CSS_IDASH_SECTION}#special_exams"
- ,
- constructor: edx.instructor_dashboard.proctoring.ProctoredExamAttemptView
- $element: idash_content.find ".#{CSS_IDASH_SECTION}#special_exams"
- ]
-
- sections_to_initialize.map ({constructor, $element}) ->
- # See fault isolation NOTE at top of file.
- # If an error is thrown in one section, it will not stop other sections from exectuing.
- plantTimeout 0, sections_have_loaded.waitFor ->
- new constructor $element
diff --git a/lms/static/coffee/src/instructor_dashboard/membership.coffee b/lms/static/coffee/src/instructor_dashboard/membership.coffee
deleted file mode 100644
index 1f1febb087..0000000000
--- a/lms/static/coffee/src/instructor_dashboard/membership.coffee
+++ /dev/null
@@ -1,740 +0,0 @@
-###
-Membership Section
-
-imports from other modules.
-wrap in (-> ... apply) to defer evaluation
-such that the value can be defined later than this assignment (file load order).
-###
-
-plantTimeout = -> window.InstructorDashboard.util.plantTimeout.apply this, arguments
-std_ajax_err = -> window.InstructorDashboard.util.std_ajax_err.apply this, arguments
-emailStudents = false
-
-
-class MemberListWidget
- # create a MemberListWidget `$container` is a jquery object to embody.
- # `params` holds template parameters. `params` should look like the defaults below.
- constructor: (@$container, params={}) ->
- params = _.defaults params,
- title: "Member List"
- info: """
- Use this list to manage members.
- """
- labels: ["field1", "field2", "field3"]
- add_placeholder: "Enter name"
- add_btn_label: "Add Member"
- add_handler: (input) ->
-
- template_html = $("#member-list-widget-template").html()
- @$container.html Mustache.render template_html, params
-
- # bind add button
- @$('input[type="button"].add').click =>
- params.add_handler? @$('.add-field').val()
-
- # clear the input text field
- clear_input: -> @$('.add-field').val ''
-
- # clear all table rows
- clear_rows: -> @$('table tbody').empty()
-
- # takes a table row as an array items are inserted as text, unless detected
- # as a jquery objects in which case they are inserted directly. if an
- # element is a jquery object
- add_row: (row_array) ->
- $tbody = @$('table tbody')
- $tr = $ '
'
- for item in row_array
- $td = $ '
'
- if item instanceof jQuery
- $td.append item
- else
- $td.text item
- $tr.append $td
- $tbody.append $tr
-
- # local selector
- $: (selector) ->
- if @debug?
- s = @$container.find selector
- if s?.length != 1
- console.warn "local selector '#{selector}' found (#{s.length}) results"
- s
- else
- @$container.find selector
-
-
-class AuthListWidget extends MemberListWidget
- constructor: ($container, @rolename, @$error_section) ->
- super $container,
- title: $container.data 'display-name'
- info: $container.data 'info-text'
- labels: [gettext("Username"), gettext("Email"), gettext("Revoke access")]
- add_placeholder: gettext("Enter username or email")
- add_btn_label: $container.data 'add-button-label'
- add_handler: (input) => @add_handler input
-
- @debug = true
- @list_endpoint = $container.data 'list-endpoint'
- @modify_endpoint = $container.data 'modify-endpoint'
- unless @rolename?
- throw "AuthListWidget missing @rolename"
-
- @reload_list()
-
- # action to do when is reintroduced into user's view
- re_view: ->
- @clear_errors()
- @clear_input()
- @reload_list()
-
- # handle clicks on the add button
- add_handler: (input) ->
- if input? and input isnt ''
- @modify_member_access input, 'allow', (error) =>
- # abort on error
- return @show_errors error unless error is null
- @clear_errors()
- @clear_input()
- @reload_list()
- else
- @show_errors gettext "Please enter a username or email."
-
- # reload the list of members
- reload_list: ->
- # @clear_rows()
- @get_member_list (error, member_list) =>
- # abort on error
- return @show_errors error unless error is null
-
- # only show the list of there are members
- @clear_rows()
-
- # use _.each instead of 'for' so that member
- # is bound in the button callback.
- _.each member_list, (member) =>
- # if there are members, show the list
-
- # create revoke button and insert it into the row
- label_trans = gettext("Revoke access")
- $revoke_btn = $ _.template('
<%- label %>
')({label: label_trans}),
- class: 'revoke'
- $revoke_btn.click =>
- @modify_member_access member.email, 'revoke', (error) =>
- # abort on error
- return @show_errors error unless error is null
- @clear_errors()
- @reload_list()
- @add_row [member.username, member.email, $revoke_btn]
-
- # clear error display
- clear_errors: -> @$error_section?.text ''
-
- # set error display
- show_errors: (msg) -> @$error_section?.text msg
-
- # send ajax request to list members
- # `cb` is called with cb(error, member_list)
- get_member_list: (cb) ->
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @list_endpoint
- data: rolename: @rolename
- success: (data) => cb? null, data[@rolename]
- error: std_ajax_err =>
- `// Translators: A rolename appears this sentence. A rolename is something like "staff" or "beta tester".`
- cb? gettext("Error fetching list for role") + " '#{@rolename}'"
-
- # send ajax request to modify access
- # (add or remove them from the list)
- # `action` can be 'allow' or 'revoke'
- # `cb` is called with cb(error, data)
- modify_member_access: (unique_student_identifier, action, cb) ->
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @modify_endpoint
- data:
- unique_student_identifier: unique_student_identifier
- rolename: @rolename
- action: action
- success: (data) => @member_response data
- error: std_ajax_err => cb? gettext "Error changing user's permissions."
-
- member_response: (data) ->
- @clear_errors()
- @clear_input()
- if data.userDoesNotExist
- msg = gettext("Could not find a user with username or email address '<%- identifier %>'.")
- @show_errors _.template(msg, {identifier: data.unique_student_identifier})
- else if data.inactiveUser
- msg = gettext("Error: User '<%- username %>' has not yet activated their account. Users must create and activate their accounts before they can be assigned a role.")
- @show_errors _.template(msg, {username: data.unique_student_identifier})
- else if data.removingSelfAsInstructor
- @show_errors gettext "Error: You cannot remove yourself from the Instructor group!"
- else
- @reload_list()
-
-class @AutoEnrollmentViaCsv
- constructor: (@$container) ->
- # Wrapper for the AutoEnrollmentViaCsv subsection.
- # This object handles buttons, success and failure reporting,
- # and server communication.
- @$student_enrollment_form = @$container.find("#student-auto-enroll-form")
- @$enrollment_signup_button = @$container.find("#submitBtn-auto_enroll_csv")
- @$students_list_file = @$container.find("input[name='students_list']")
- @$csrf_token = @$container.find("input[name='csrfmiddlewaretoken']")
- @$results = @$container.find("div.results")
- @$browse_button = @$container.find("#browseBtn-auto-enroll")
- @$browse_file = @$container.find("#browseFile")
-
- @processing = false
-
- @$browse_button.on "change", (event) =>
- if event.currentTarget.files.length == 1
- @$browse_file.val(event.currentTarget.value.substring(event.currentTarget.value.lastIndexOf("\\") + 1))
-
- # attach click handler for @$enrollment_signup_button
- @$enrollment_signup_button.click =>
- @$student_enrollment_form.submit (event) =>
- if @processing
- return false
-
- @processing = true
-
- event.preventDefault()
- data = new FormData(event.currentTarget)
- $.ajax
- dataType: 'json'
- type: 'POST'
- url: event.currentTarget.action
- data: data
- processData: false
- contentType: false
- success: (data) =>
- @processing = false
- @display_response data
-
- return false
-
- display_response: (data_from_server) ->
- @$results.empty()
- errors = []
- warnings = []
- result_from_server_is_success = true
-
- if data_from_server.general_errors.length
- result_from_server_is_success = false
- for general_error in data_from_server.general_errors
- general_error['is_general_error'] = true
- errors.push general_error
-
- if data_from_server.row_errors.length
- result_from_server_is_success = false
- for error in data_from_server.row_errors
- error['is_general_error'] = false
- errors.push error
-
- if data_from_server.warnings.length
- result_from_server_is_success = false
- for warning in data_from_server.warnings
- warning['is_general_error'] = false
- warnings.push warning
-
- render_response = (title, message, type, student_results) =>
- details = []
- for student_result in student_results
- if student_result.is_general_error
- details.push student_result.response
- else
- response_message = student_result.username + ' ('+ student_result.email + '): ' + ' (' + student_result.response + ')'
- details.push response_message
-
- @$results.append @render_notification_view type, title, message, details
-
- if errors.length
- render_response gettext('Errors'), gettext("The following errors were generated:"), 'error', errors
- if warnings.length
- render_response gettext('Warnings'), gettext("The following warnings were generated:"), 'warning', warnings
- if result_from_server_is_success
- render_response gettext('Success'), gettext("All accounts were created successfully."), 'confirmation', []
-
- render_notification_view: (type, title, message, details) ->
- notification_model = new NotificationModel()
- notification_model.set({
- 'type': type,
- 'title': title,
- 'message': message,
- 'details': details,
- });
- view = new NotificationView(model:notification_model);
- view.render()
- return view.$el.html()
-
-class BetaTesterBulkAddition
- constructor: (@$container) ->
- # gather elements
- @$identifier_input = @$container.find("textarea[name='student-ids-for-beta']")
- @$btn_beta_testers = @$container.find("input[name='beta-testers']")
- @$checkbox_autoenroll = @$container.find("input[name='auto-enroll']")
- @$checkbox_emailstudents = @$container.find("input[name='email-students-beta']")
- @$task_response = @$container.find(".request-response")
- @$request_response_error = @$container.find(".request-response-error")
-
- # click handlers
- @$btn_beta_testers.click (event) =>
- emailStudents = @$checkbox_emailstudents.is(':checked')
- autoEnroll = @$checkbox_autoenroll.is(':checked')
- send_data =
- action: $(event.target).data('action') # 'add' or 'remove'
- identifiers: @$identifier_input.val()
- email_students: emailStudents
- auto_enroll: autoEnroll
-
- $.ajax
- dataType: 'json'
- type: 'POST'
- url: @$btn_beta_testers.data 'endpoint'
- data: send_data
- success: (data) => @display_response data
- error: std_ajax_err => @fail_with_error gettext "Error adding/removing users as beta testers."
-
- # clear the input text field
- clear_input: ->
- @$identifier_input.val ''
- # default for the checkboxes should be checked
- @$checkbox_emailstudents.attr('checked', true)
- @$checkbox_autoenroll.attr('checked', true)
-
- fail_with_error: (msg) ->
- console.warn msg
- @clear_input()
- @$task_response.empty()
- @$request_response_error.empty()
- @$request_response_error.text msg
-
- display_response: (data_from_server) ->
- @clear_input()
- @$task_response.empty()
- @$request_response_error.empty()
- errors = []
- successes = []
- no_users = []
- for student_results in data_from_server.results
- if student_results.userDoesNotExist
- no_users.push student_results
- else if student_results.error
- errors.push student_results
- else
- successes.push student_results
-
- render_list = (label, ids) =>
- task_res_section = $ '', class: 'request-res-section'
- task_res_section.append $ '', text: label
- ids_list = $ '
'
- task_res_section.append ids_list
-
- for identifier in ids
- ids_list.append $ '', text: identifier
-
- @$task_response.append task_res_section
-
- if successes.length and data_from_server.action is 'add'
- `// Translators: A list of users appears after this sentence`
- render_list gettext("These users were successfully added as beta testers:"), (sr.identifier for sr in successes)
-
- if successes.length and data_from_server.action is 'remove'
- `// Translators: A list of users appears after this sentence`
- render_list gettext("These users were successfully removed as beta testers:"), (sr.identifier for sr in successes)
-
- if errors.length and data_from_server.action is 'add'
- `// Translators: A list of users appears after this sentence`
- render_list gettext("These users were not added as beta testers:"), (sr.identifier for sr in errors)
-
- if errors.length and data_from_server.action is 'remove'
- `// Translators: A list of users appears after this sentence`
- render_list gettext("These users were not removed as beta testers:"), (sr.identifier for sr in errors)
-
- if no_users.length
- no_users.push $ gettext("Users must create and activate their account before they can be promoted to beta tester.")
- `// Translators: A list of identifiers (which are email addresses and/or usernames) appears after this sentence`
- render_list gettext("Could not find users associated with the following identifiers:"), (sr.identifier for sr in no_users)
-
-# Wrapper for the batch enrollment subsection.
-# This object handles buttons, success and failure reporting,
-# and server communication.
-class BatchEnrollment
- constructor: (@$container) ->
- # gather elements
- @$identifier_input = @$container.find("textarea[name='student-ids']")
- @$enrollment_button = @$container.find(".enrollment-button")
- @$is_course_white_label = @$container.find("#is_course_white_label").val()
- @$reason_field = @$container.find("textarea[name='reason-field']")
- @$checkbox_autoenroll = @$container.find("input[name='auto-enroll']")
- @$checkbox_emailstudents = @$container.find("input[name='email-students']")
- @$task_response = @$container.find(".request-response")
- @$request_response_error = @$container.find(".request-response-error")
-
- # attach click handler for enrollment buttons
- @$enrollment_button.click (event) =>
- if @$is_course_white_label == 'True'
- if not @$reason_field.val()
- @fail_with_error gettext "Reason field should not be left blank."
- return false
-
- emailStudents = @$checkbox_emailstudents.is(':checked')
- send_data =
- action: $(event.target).data('action') # 'enroll' or 'unenroll'
- identifiers: @$identifier_input.val()
- auto_enroll: @$checkbox_autoenroll.is(':checked')
- email_students: emailStudents
- reason: @$reason_field.val()
-
- $.ajax
- dataType: 'json'
- type: 'POST'
- url: $(event.target).data 'endpoint'
- data: send_data
- success: (data) => @display_response data
- error: std_ajax_err => @fail_with_error gettext "Error enrolling/unenrolling users."
-
-
- # clear the input text field
- clear_input: ->
- @$identifier_input.val ''
- @$reason_field.val ''
- # default for the checkboxes should be checked
- @$checkbox_emailstudents.attr('checked', true)
- @$checkbox_autoenroll.attr('checked', true)
-
- fail_with_error: (msg) ->
- console.warn msg
- @clear_input()
- @$task_response.empty()
- @$request_response_error.empty()
- @$request_response_error.text msg
-
- display_response: (data_from_server) ->
- @clear_input()
- @$task_response.empty()
- @$request_response_error.empty()
-
- # these results arrays contain student_results
- # only populated arrays will be rendered
- #
- # invalid identifiers
- invalid_identifier = []
- # students for which there was an error during the action
- errors = []
- # students who are now enrolled in the course
- enrolled = []
- # students who are now allowed to enroll in the course
- allowed = []
- # students who will be autoenrolled on registration
- autoenrolled = []
- # students who are now not enrolled in the course
- notenrolled = []
- # students who were not enrolled or allowed prior to unenroll action
- notunenrolled = []
-
- # categorize student results into the above arrays.
- for student_results in data_from_server.results
- # for a successful action.
- # student_results is of the form {
- # "identifier": "jd405@edx.org",
- # "before": {
- # "enrollment": true,
- # "auto_enroll": false,
- # "user": true,
- # "allowed": false
- # }
- # "after": {
- # "enrollment": true,
- # "auto_enroll": false,
- # "user": true,
- # "allowed": false
- # },
- # }
- #
- # for an action error.
- # student_results is of the form {
- # 'identifier': identifier,
- # # then one of:
- # 'error': True,
- # 'invalidIdentifier': True # if identifier can't find a valid User object and doesn't pass validate_email
- # }
-
- if student_results.invalidIdentifier
- invalid_identifier.push student_results
-
- else if student_results.error
- errors.push student_results
-
- else if student_results.after.enrollment
- enrolled.push student_results
-
- else if student_results.after.allowed
- if student_results.after.auto_enroll
- autoenrolled.push student_results
- else
- allowed.push student_results
-
- # The instructor is trying to unenroll someone who is not enrolled or allowed to enroll; non-sensical action.
- else if data_from_server.action is 'unenroll' and not (student_results.before.enrollment) and not (student_results.before.allowed)
- notunenrolled.push student_results
-
- else if not student_results.after.enrollment
- notenrolled.push student_results
-
- else
- console.warn 'student results not reported to user'
- console.warn student_results
-
- # render populated result arrays
- render_list = (label, ids) =>
- task_res_section = $ '', class: 'request-res-section'
- task_res_section.append $ '', text: label
- ids_list = $ '
'
- task_res_section.append ids_list
-
- for identifier in ids
- ids_list.append $ '', text: identifier
-
- @$task_response.append task_res_section
-
- if invalid_identifier.length
- render_list gettext("The following email addresses and/or usernames are invalid:"), (sr.identifier for sr in invalid_identifier)
-
- if errors.length
- errors_label = do ->
- if data_from_server.action is 'enroll'
- "There was an error enrolling:"
- else if data_from_server.action is 'unenroll'
- "There was an error unenrolling:"
- else
- console.warn "unknown action from server '#{data_from_server.action}'"
- "There was an error processing:"
-
- for student_results in errors
- render_list errors_label, (sr.identifier for sr in errors)
-
- if enrolled.length and emailStudents
- render_list gettext("Successfully enrolled and sent email to the following users:"), (sr.identifier for sr in enrolled)
-
- if enrolled.length and not emailStudents
- `// Translators: A list of users appears after this sentence`
- render_list gettext("Successfully enrolled the following users:"), (sr.identifier for sr in enrolled)
-
- # Student hasn't registered so we allow them to enroll
- if allowed.length and emailStudents
- `// Translators: A list of users appears after this sentence`
- render_list gettext("Successfully sent enrollment emails to the following users. They will be allowed to enroll once they register:"),
- (sr.identifier for sr in allowed)
-
- # Student hasn't registered so we allow them to enroll
- if allowed.length and not emailStudents
- `// Translators: A list of users appears after this sentence`
- render_list gettext("These users will be allowed to enroll once they register:"),
- (sr.identifier for sr in allowed)
-
- # Student hasn't registered so we allow them to enroll with autoenroll
- if autoenrolled.length and emailStudents
- `// Translators: A list of users appears after this sentence`
- render_list gettext("Successfully sent enrollment emails to the following users. They will be enrolled once they register:"),
- (sr.identifier for sr in autoenrolled)
-
- # Student hasn't registered so we allow them to enroll with autoenroll
- if autoenrolled.length and not emailStudents
- `// Translators: A list of users appears after this sentence`
- render_list gettext("These users will be enrolled once they register:"),
- (sr.identifier for sr in autoenrolled)
-
- if notenrolled.length and emailStudents
- `// Translators: A list of users appears after this sentence`
- render_list gettext("Emails successfully sent. The following users are no longer enrolled in the course:"),
- (sr.identifier for sr in notenrolled)
-
- if notenrolled.length and not emailStudents
- `// Translators: A list of users appears after this sentence`
- render_list gettext("The following users are no longer enrolled in the course:"),
- (sr.identifier for sr in notenrolled)
-
- if notunenrolled.length
- `// Translators: A list of users appears after this sentence. This situation arises when a staff member tries to unenroll a user who is not currently enrolled in this course.`
- render_list gettext("These users were not affiliated with the course so could not be unenrolled:"),
- (sr.identifier for sr in notunenrolled)
-
-# Wrapper for auth list subsection.
-# manages a list of users who have special access.
-# these could be instructors, staff, beta users, or forum roles.
-# uses slickgrid to display list.
-class AuthList
- # rolename is one of ['instructor', 'staff'] for instructor_staff endpoints
- # rolename is the name of Role for forums for the forum endpoints
- constructor: (@$container, @rolename) ->
- # gather elements
- @$display_table = @$container.find('.auth-list-table')
- @$request_response_error = @$container.find('.request-response-error')
- @$add_section = @$container.find('.auth-list-add')
- @$allow_field = @$add_section.find("input[name='email']")
- @$allow_button = @$add_section.find("input[name='allow']")
-
- # attach click handler
- @$allow_button.click =>
- @access_change @$allow_field.val(), 'allow', => @reload_auth_list()
- @$allow_field.val ''
-
- @reload_auth_list()
-
- # fetch and display list of users who match criteria
- reload_auth_list: ->
- # helper function to display server data in the list
- load_auth_list = (data) =>
- # clear existing data
- @$request_response_error.empty()
- @$display_table.empty()
-
- # setup slickgrid
- options =
- enableCellNavigation: true
- enableColumnReorder: false
- # autoHeight: true
- forceFitColumns: true
-
- # this is a hack to put a button/link in a slick grid cell
- # if you change columns, then you must update
- # WHICH_CELL_IS_REVOKE to have the index
- # of the revoke column (left to right).
- WHICH_CELL_IS_REVOKE = 3
- columns = [
- id: 'username'
- field: 'username'
- name: 'Username'
- ,
- id: 'email'
- field: 'email'
- name: 'Email'
- ,
- id: 'first_name'
- field: 'first_name'
- name: 'First Name'
- ,
- # id: 'last_name'
- # field: 'last_name'
- # name: 'Last Name'
- # ,
- id: 'revoke'
- field: 'revoke'
- name: 'Revoke'
- formatter: (row, cell, value, columnDef, dataContext) ->
- "Revoke Access"
- ]
-
- table_data = data[@rolename]
-
- $table_placeholder = $ '', class: 'slickgrid'
- @$display_table.append $table_placeholder
- grid = new Slick.Grid($table_placeholder, table_data, columns, options)
-
- # click handler part of the revoke button/link hack.
- grid.onClick.subscribe (e, args) =>
- item = args.grid.getDataItem(args.row)
- if args.cell is WHICH_CELL_IS_REVOKE
- @access_change item.email, 'revoke', => @reload_auth_list()
-
- # fetch data from the endpoint
- # the endpoint comes from data-endpoint of the table
- $.ajax
- dataType: 'json'
- type: 'POST'
- url: @$display_table.data 'endpoint'
- data: rolename: @rolename
- success: load_auth_list
- error: std_ajax_err => @$request_response_error.text "Error fetching list for '#{@rolename}'"
-
-
- # slickgrid's layout collapses when rendered
- # in an invisible div. use this method to reload
- # the AuthList widget
- refresh: ->
- @$display_table.empty()
- @reload_auth_list()
-
- # update the access of a user.
- # (add or remove them from the list)
- # action should be one of ['allow', 'revoke']
- access_change: (email, action, cb) ->
- $.ajax
- dataType: 'json'
- type: 'POST'
- url: @$add_section.data 'endpoint'
- data:
- email: email
- rolename: @rolename
- action: action
- success: (data) -> cb?(data)
- error: std_ajax_err => @$request_response_error.text gettext "Error changing user's permissions."
-
-
-# Membership Section
-class Membership
- # enable subsections.
- constructor: (@$section) ->
- # attach self to html
- # so that instructor_dashboard.coffee can find this object
- # to call event handlers like 'onClickTitle'
- @$section.data 'wrapper', @
-
- # isolate # initialize BatchEnrollment subsection
- plantTimeout 0, => new BatchEnrollment @$section.find '.batch-enrollment'
-
- # isolate # initialize AutoEnrollmentViaCsv subsection
- plantTimeout 0, => new AutoEnrollmentViaCsv @$section.find '.auto_enroll_csv'
-
- # initialize BetaTesterBulkAddition subsection
- plantTimeout 0, => new BetaTesterBulkAddition @$section.find '.batch-beta-testers'
-
- # gather elements
- @$list_selector = @$section.find 'select#member-lists-selector'
- @$auth_list_containers = @$section.find '.auth-list-container'
- @$auth_list_errors = @$section.find '.member-lists-management .request-response-error'
-
- # initialize & store AuthList subsections
- # one for each .auth-list-container in the section.
- @auth_lists = _.map (@$auth_list_containers), (auth_list_container) =>
- rolename = $(auth_list_container).data 'rolename'
- new AuthListWidget $(auth_list_container), rolename, @$auth_list_errors
-
- # populate selector
- @$list_selector.empty()
- for auth_list in @auth_lists
- @$list_selector.append $ '',
- text: auth_list.$container.data 'display-name'
- data:
- auth_list: auth_list
- if @auth_lists.length is 0
- @$list_selector.hide()
-
- @$list_selector.change =>
- $opt = @$list_selector.children('option:selected')
- return unless $opt.length > 0
- for auth_list in @auth_lists
- auth_list.$container.removeClass 'active'
- auth_list = $opt.data('auth_list')
- auth_list.$container.addClass 'active'
- auth_list.re_view()
-
- # one-time first selection of top list.
- @$list_selector.change()
-
- # handler for when the section title is clicked.
- onClickTitle: ->
-
-
-# export for use
-# create parent namespaces if they do not already exist.
-_.defaults window, InstructorDashboard: {}
-_.defaults window.InstructorDashboard, sections: {}
-_.defaults window.InstructorDashboard.sections,
- Membership: Membership
diff --git a/lms/static/coffee/src/instructor_dashboard/metrics.coffee b/lms/static/coffee/src/instructor_dashboard/metrics.coffee
deleted file mode 100644
index ec28e48670..0000000000
--- a/lms/static/coffee/src/instructor_dashboard/metrics.coffee
+++ /dev/null
@@ -1,25 +0,0 @@
-# METRICS Section
-
-# imports from other modules.
-# wrap in (-> ... apply) to defer evaluation
-# such that the value can be defined later than this assignment (file load order).
-plantTimeout = -> window.InstructorDashboard.util.plantTimeout.apply this, arguments
-std_ajax_err = -> window.InstructorDashboard.util.std_ajax_err.apply this, arguments
-
-#Metrics Section
-class Metrics
- constructor: (@$section) ->
- @$section.data 'wrapper', @
-
-
- # handler for when the section title is clicked.
- onClickTitle: ->
-
-# export for use
-# create parent namespaces if they do not already exist.
-# abort if underscore can not be found.
-if _?
- _.defaults window, InstructorDashboard: {}
- _.defaults window.InstructorDashboard, sections: {}
- _.defaults window.InstructorDashboard.sections,
- Metrics: Metrics
diff --git a/lms/static/coffee/src/instructor_dashboard/send_email.coffee b/lms/static/coffee/src/instructor_dashboard/send_email.coffee
deleted file mode 100644
index dab903ca16..0000000000
--- a/lms/static/coffee/src/instructor_dashboard/send_email.coffee
+++ /dev/null
@@ -1,197 +0,0 @@
-###
-Email Section
-
-imports from other modules.
-wrap in (-> ... apply) to defer evaluation
-such that the value can be defined later than this assignment (file load order).
-###
-
-# Load utilities
-plantTimeout = -> window.InstructorDashboard.util.plantTimeout.apply this, arguments
-std_ajax_err = -> window.InstructorDashboard.util.std_ajax_err.apply this, arguments
-PendingInstructorTasks = -> window.InstructorDashboard.util.PendingInstructorTasks
-create_task_list_table = -> window.InstructorDashboard.util.create_task_list_table.apply this, arguments
-create_email_content_table = -> window.InstructorDashboard.util.create_email_content_table.apply this, arguments
-create_email_message_views = -> window.InstructorDashboard.util.create_email_message_views.apply this, arguments
-KeywordValidator = -> window.InstructorDashboard.util.KeywordValidator
-
-class @SendEmail
- constructor: (@$container) ->
- # gather elements
- @$emailEditor = XBlock.initializeBlock($('.xblock-studio_view'));
- @$send_to = @$container.find("input[name='send_to']")
- @$cohort_targets = @$send_to.filter('[value^="cohort:"]')
- @$subject = @$container.find("input[name='subject']")
- @$btn_send = @$container.find("input[name='send']")
- @$task_response = @$container.find(".request-response")
- @$request_response_error = @$container.find(".request-response-error")
- @$content_request_response_error = @$container.find(".content-request-response-error")
- @$history_request_response_error = @$container.find(".history-request-response-error")
- @$btn_task_history_email = @$container.find("input[name='task-history-email']")
- @$btn_task_history_email_content = @$container.find("input[name='task-history-email-content']")
- @$table_task_history_email = @$container.find(".task-history-email-table")
- @$table_email_content_history = @$container.find(".content-history-email-table")
- @$email_content_table_inner = @$container.find(".content-history-table-inner")
- @$email_messages_wrapper = @$container.find(".email-messages-wrapper")
-
- # attach click handlers
-
- @$btn_send.click =>
- subject = @$subject.val()
- body = @$emailEditor.save()['data']
- targets = []
- @$send_to.filter(':checked').each ->
- targets.push(this.value)
-
- if subject == ""
- alert gettext("Your message must have a subject.")
-
- else if body == ""
- alert gettext("Your message cannot be blank.")
-
- else if targets.length == 0
- alert gettext("Your message must have at least one target.")
-
- else
- # Validation for keyword substitution
- validation = KeywordValidator().validate_string body
- if not validation.is_valid
- message = gettext("There are invalid keywords in your email. Check the following keywords and try again.")
- message += "\n" + validation.invalid_keywords.join('\n')
- alert message
- return
-
- display_target = (value) ->
- if value == "myself"
- gettext("Yourself")
- else if value == "staff"
- gettext("Everyone who has staff privileges in this course")
- else if value == "learners"
- gettext("All learners who are enrolled in this course")
- else
- gettext("All learners in the {cohort_name} cohort").replace('{cohort_name}', value.slice(value.indexOf(':')+1))
- success_message = gettext("Your email message was successfully queued for sending. In courses with a large number of learners, email messages to learners might take up to an hour to be sent.")
- confirm_message = gettext("You are sending an email message with the subject {subject} to the following recipients.")
- for target in targets
- confirm_message += "\n-" + display_target(target)
- confirm_message += "\n\n" + gettext("Is this OK?")
- full_confirm_message = confirm_message.replace('{subject}', subject)
-
- if confirm full_confirm_message
-
- send_data =
- action: 'send'
- send_to: JSON.stringify(targets)
- subject: subject
- message: body
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$btn_send.data 'endpoint'
- data: send_data
- success: (data) =>
- @display_response success_message
-
- error: std_ajax_err =>
- @fail_with_error gettext('Error sending email.')
-
- else
- @task_response.empty()
- @$request_response_error.empty()
-
- # list task history for email
- @$btn_task_history_email.click =>
- url = @$btn_task_history_email.data 'endpoint'
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: url
- success: (data) =>
- if data.tasks.length
- create_task_list_table @$table_task_history_email, data.tasks
- else
- @$history_request_response_error.text gettext("There is no email history for this course.")
- # Enable the msg-warning css display
- @$history_request_response_error.css({"display":"block"})
- error: std_ajax_err =>
- @$history_request_response_error.text gettext("There was an error obtaining email task history for this course.")
-
- # List content history for emails sent
- @$btn_task_history_email_content.click =>
- url = @$btn_task_history_email_content.data 'endpoint'
- $.ajax
- type: 'POST'
- dataType: 'json'
- url : url
- success: (data) =>
- if data.emails.length
- create_email_content_table @$table_email_content_history, @$email_content_table_inner, data.emails
- create_email_message_views @$email_messages_wrapper, data.emails
- else
- @$content_request_response_error.text gettext("There is no email history for this course.")
- @$content_request_response_error.css({"display":"block"})
- error: std_ajax_err =>
- @$content_request_response_error.text gettext("There was an error obtaining email content history for this course.")
-
- @$send_to.change =>
- # Ensure invalid combinations are disabled
- if $('input#target_learners:checked').length
- # If all is selected, cohorts can't be
- @$cohort_targets.each ->
- this.checked = false
- this.disabled = true
- true
- else
- @$cohort_targets.each ->
- this.disabled = false
- true
-
- # Also, keep the sent_to_list div updated
- targets = []
- $('input[name="send_to"]:checked+label').each ->
- # Only use the first line, even if a subheading is present
- targets.push(this.innerText.replace(/\s*\n.*/g,''))
- $(".send_to_list").text(gettext("Send to:") + " " + targets.join(", "))
-
-
- fail_with_error: (msg) ->
- console.warn msg
- @$task_response.empty()
- @$request_response_error.empty()
- @$request_response_error.text msg
- $(".msg-confirm").css({"display":"none"})
-
- display_response: (data_from_server) ->
- @$task_response.empty()
- @$request_response_error.empty()
- @$task_response.text(data_from_server)
- $(".msg-confirm").css({"display":"block"})
-
-
-# Email Section
-class Email
- # enable subsections.
- constructor: (@$section) ->
- # attach self to html so that instructor_dashboard.coffee can find
- # this object to call event handlers like 'onClickTitle'
- @$section.data 'wrapper', @
-
- # isolate # initialize SendEmail subsection
- plantTimeout 0, => new SendEmail @$section.find '.send-email'
-
- @instructor_tasks = new (PendingInstructorTasks()) @$section
-
- # handler for when the section title is clicked.
- onClickTitle: -> @instructor_tasks.task_poller.start()
-
- # handler for when the section is closed
- onExit: -> @instructor_tasks.task_poller.stop()
-
-
-# export for use
-# create parent namespaces if they do not already exist.
-_.defaults window, InstructorDashboard: {}
-_.defaults window.InstructorDashboard, sections: {}
-_.defaults window.InstructorDashboard.sections,
- Email: Email
diff --git a/lms/static/coffee/src/instructor_dashboard/student_admin.coffee b/lms/static/coffee/src/instructor_dashboard/student_admin.coffee
deleted file mode 100644
index c9de7cba74..0000000000
--- a/lms/static/coffee/src/instructor_dashboard/student_admin.coffee
+++ /dev/null
@@ -1,398 +0,0 @@
-###
-Student Admin Section
-
-imports from other modules.
-wrap in (-> ... apply) to defer evaluation
-such that the value can be defined later than this assignment (file load order).
-###
-
-# Load utilities
-std_ajax_err = -> window.InstructorDashboard.util.std_ajax_err.apply this, arguments
-create_task_list_table = -> window.InstructorDashboard.util.create_task_list_table.apply this, arguments
-PendingInstructorTasks = -> window.InstructorDashboard.util.PendingInstructorTasks
-
-
-# get jquery element and assert its existance
-find_and_assert = ($root, selector) ->
- item = $root.find selector
- if item.length != 1
- console.error "element selection failed for '#{selector}' resulted in length #{item.length}"
- throw "Failed Element Selection"
- else
- item
-
-
-class @StudentAdmin
- constructor: (@$section) ->
- # attach self to html so that instructor_dashboard.coffee can find
- # this object to call event handlers like 'onClickTitle'
- @$section.data 'wrapper', @
-
- # gather buttons
- # some buttons are optional because they can be flipped by the instructor task feature switch
- # student-specific
- @$field_student_select_progress = find_and_assert @$section, "input[name='student-select-progress']"
- @$field_student_select_grade = find_and_assert @$section, "input[name='student-select-grade']"
- @$progress_link = find_and_assert @$section, "a.progress-link"
- @$field_problem_select_single = find_and_assert @$section, "input[name='problem-select-single']"
- @$btn_reset_attempts_single = find_and_assert @$section, "input[name='reset-attempts-single']"
- @$btn_delete_state_single = @$section.find "input[name='delete-state-single']"
- @$btn_rescore_problem_single = @$section.find "input[name='rescore-problem-single']"
- @$btn_task_history_single = @$section.find "input[name='task-history-single']"
- @$table_task_history_single = @$section.find ".task-history-single-table"
-
- # entrance-exam-specific
- @$field_entrance_exam_student_select_grade = @$section.find "input[name='entrance-exam-student-select-grade']"
- @$btn_reset_entrance_exam_attempts = @$section.find "input[name='reset-entrance-exam-attempts']"
- @$btn_delete_entrance_exam_state = @$section.find "input[name='delete-entrance-exam-state']"
- @$btn_rescore_entrance_exam = @$section.find "input[name='rescore-entrance-exam']"
- @$btn_skip_entrance_exam = @$section.find "input[name='skip-entrance-exam']"
- @$btn_entrance_exam_task_history = @$section.find "input[name='entrance-exam-task-history']"
- @$table_entrance_exam_task_history = @$section.find ".entrance-exam-task-history-table"
-
- # course-specific
- @$field_problem_select_all = @$section.find "input[name='problem-select-all']"
- @$btn_reset_attempts_all = @$section.find "input[name='reset-attempts-all']"
- @$btn_rescore_problem_all = @$section.find "input[name='rescore-problem-all']"
- @$btn_task_history_all = @$section.find "input[name='task-history-all']"
- @$table_task_history_all = @$section.find ".task-history-all-table"
- @instructor_tasks = new (PendingInstructorTasks()) @$section
-
- # response areas
- @$request_response_error_progress = find_and_assert @$section, ".student-specific-container .request-response-error"
- @$request_response_error_grade = find_and_assert @$section, ".student-grade-container .request-response-error"
- @$request_response_error_ee = @$section.find ".entrance-exam-grade-container .request-response-error"
- @$request_response_error_all = @$section.find ".course-specific-container .request-response-error"
-
- # attach click handlers
-
- # go to student progress page
- @$progress_link.click (e) =>
- e.preventDefault()
- unique_student_identifier = @$field_student_select_progress.val()
- if not unique_student_identifier
- return @$request_response_error_progress.text gettext("Please enter a student email address or username.")
- error_message = gettext("Error getting student progress url for '<%= student_id %>'. Make sure that the student identifier is spelled correctly.")
- full_error_message = _.template(error_message)({student_id: unique_student_identifier})
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$progress_link.data 'endpoint'
- data: unique_student_identifier: unique_student_identifier
- success: @clear_errors_then (data) ->
- window.location = data.progress_url
- error: std_ajax_err => @$request_response_error_progress.text full_error_message
-
- # reset attempts for student on problem
- @$btn_reset_attempts_single.click =>
- unique_student_identifier = @$field_student_select_grade.val()
- problem_to_reset = @$field_problem_select_single.val()
- if not unique_student_identifier
- return @$request_response_error_grade.text gettext("Please enter a student email address or username.")
- if not problem_to_reset
- return @$request_response_error_grade.text gettext("Please enter a problem location.")
- send_data =
- unique_student_identifier: unique_student_identifier
- problem_to_reset: problem_to_reset
- delete_module: false
- success_message = gettext("Success! Problem attempts reset for problem '<%= problem_id %>' and student '<%= student_id %>'.")
- error_message = gettext("Error resetting problem attempts for problem '<%= problem_id %>' and student '<%= student_id %>'. Make sure that the problem and student identifiers are complete and correct.")
- full_success_message = _.template(success_message)({problem_id: problem_to_reset, student_id: unique_student_identifier})
- full_error_message = _.template(error_message)({problem_id: problem_to_reset, student_id: unique_student_identifier})
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$btn_reset_attempts_single.data 'endpoint'
- data: send_data
- success: @clear_errors_then -> alert full_success_message
- error: std_ajax_err => @$request_response_error_grade.text full_error_message
-
- # delete state for student on problem
- @$btn_delete_state_single.click =>
- unique_student_identifier = @$field_student_select_grade.val()
- problem_to_reset = @$field_problem_select_single.val()
- if not unique_student_identifier
- return @$request_response_error_grade.text gettext("Please enter a student email address or username.")
- if not problem_to_reset
- return @$request_response_error_grade.text gettext("Please enter a problem location.")
- confirm_message = gettext("Delete student '<%= student_id %>'s state on problem '<%= problem_id %>'?")
- full_confirm_message = _.template(confirm_message)({student_id: unique_student_identifier, problem_id: problem_to_reset})
-
- if window.confirm full_confirm_message
- send_data =
- unique_student_identifier: unique_student_identifier
- problem_to_reset: problem_to_reset
- delete_module: true
- error_message = gettext("Error deleting student '<%= student_id %>'s state on problem '<%= problem_id %>'. Make sure that the problem and student identifiers are complete and correct.")
- full_error_message = _.template(error_message)({student_id: unique_student_identifier, problem_id: problem_to_reset})
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$btn_delete_state_single.data 'endpoint'
- data: send_data
- success: @clear_errors_then -> alert gettext('Module state successfully deleted.')
- error: std_ajax_err => @$request_response_error_grade.text full_error_message
- else
- # Clear error messages if "Cancel" was chosen on confirmation alert
- @clear_errors()
-
- # start task to rescore problem for student
- @$btn_rescore_problem_single.click =>
- unique_student_identifier = @$field_student_select_grade.val()
- problem_to_reset = @$field_problem_select_single.val()
- if not unique_student_identifier
- return @$request_response_error_grade.text gettext("Please enter a student email address or username.")
- if not problem_to_reset
- return @$request_response_error_grade.text gettext("Please enter a problem location.")
- send_data =
- unique_student_identifier: unique_student_identifier
- problem_to_reset: problem_to_reset
- success_message = gettext("Started rescore problem task for problem '<%= problem_id %>' and student '<%= student_id %>'. Click the 'Show Background Task History for Student' button to see the status of the task.")
- full_success_message = _.template(success_message)({student_id: unique_student_identifier, problem_id: problem_to_reset})
- error_message = gettext("Error starting a task to rescore problem '<%= problem_id %>' for student '<%= student_id %>'. Make sure that the the problem and student identifiers are complete and correct.")
- full_error_message = _.template(error_message)({student_id: unique_student_identifier, problem_id: problem_to_reset})
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$btn_rescore_problem_single.data 'endpoint'
- data: send_data
- success: @clear_errors_then -> alert full_success_message
- error: std_ajax_err => @$request_response_error_grade.text full_error_message
-
- # list task history for student+problem
- @$btn_task_history_single.click =>
- unique_student_identifier = @$field_student_select_grade.val()
- problem_to_reset = @$field_problem_select_single.val()
- if not unique_student_identifier
- return @$request_response_error_grade.text gettext("Please enter a student email address or username.")
- if not problem_to_reset
- return @$request_response_error_grade.text gettext("Please enter a problem location.")
- send_data =
- unique_student_identifier: unique_student_identifier
- problem_location_str: problem_to_reset
- error_message = gettext("Error getting task history for problem '<%= problem_id %>' and student '<%= student_id %>'. Make sure that the problem and student identifiers are complete and correct.")
- full_error_message = _.template(error_message)({student_id: unique_student_identifier, problem_id: problem_to_reset})
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$btn_task_history_single.data 'endpoint'
- data: send_data
- success: @clear_errors_then (data) =>
- create_task_list_table @$table_task_history_single, data.tasks
- error: std_ajax_err => @$request_response_error_grade.text full_error_message
-
- # reset entrance exam attempts for student
- @$btn_reset_entrance_exam_attempts.click =>
- unique_student_identifier = @$field_entrance_exam_student_select_grade.val()
- if not unique_student_identifier
- return @$request_response_error_ee.text gettext("Please enter a student email address or username.")
- send_data =
- unique_student_identifier: unique_student_identifier
- delete_module: false
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$btn_reset_entrance_exam_attempts.data 'endpoint'
- data: send_data
- success: @clear_errors_then ->
- success_message = gettext("Entrance exam attempts is being reset for student '{student_id}'.")
- full_success_message = interpolate_text(success_message, {student_id: unique_student_identifier})
- alert full_success_message
- error: std_ajax_err =>
- error_message = gettext("Error resetting entrance exam attempts for student '{student_id}'. Make sure student identifier is correct.")
- full_error_message = interpolate_text(error_message, {student_id: unique_student_identifier})
- @$request_response_error_ee.text full_error_message
-
- # start task to rescore entrance exam for student
- @$btn_rescore_entrance_exam.click =>
- unique_student_identifier = @$field_entrance_exam_student_select_grade.val()
- if not unique_student_identifier
- return @$request_response_error_ee.text gettext("Please enter a student email address or username.")
- send_data =
- unique_student_identifier: unique_student_identifier
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$btn_rescore_entrance_exam.data 'endpoint'
- data: send_data
- success: @clear_errors_then ->
- success_message = gettext("Started entrance exam rescore task for student '{student_id}'. Click the 'Show Background Task History for Student' button to see the status of the task.")
- full_success_message = interpolate_text(success_message, {student_id: unique_student_identifier})
- alert full_success_message
- error: std_ajax_err =>
- error_message = gettext("Error starting a task to rescore entrance exam for student '{student_id}'. Make sure that entrance exam has problems in it and student identifier is correct.")
- full_error_message = interpolate_text(error_message, {student_id: unique_student_identifier})
- @$request_response_error_ee.text full_error_message
-
- # Mark a student to skip entrance exam
- @$btn_skip_entrance_exam.click =>
- unique_student_identifier = @$field_entrance_exam_student_select_grade.val()
- if not unique_student_identifier
- return @$request_response_error_ee.text gettext("Enter a student's username or email address.")
- confirm_message = gettext("Do you want to allow this student ('{student_id}') to skip the entrance exam?")
- full_confirm_message = interpolate_text(confirm_message, {student_id: unique_student_identifier})
- if window.confirm full_confirm_message
- send_data =
- unique_student_identifier: unique_student_identifier
-
- $.ajax
- dataType: 'json'
- url: @$btn_skip_entrance_exam.data 'endpoint'
- data: send_data
- type: 'POST'
- success: @clear_errors_then (data) ->
- alert data.message
- error: std_ajax_err =>
- error_message = gettext("An error occurred. Make sure that the student's username or email address is correct and try again.")
- @$request_response_error_ee.text error_message
-
- # delete student state for entrance exam
- @$btn_delete_entrance_exam_state.click =>
- unique_student_identifier = @$field_entrance_exam_student_select_grade.val()
- if not unique_student_identifier
- return @$request_response_error_ee.text gettext("Please enter a student email address or username.")
- send_data =
- unique_student_identifier: unique_student_identifier
- delete_module: true
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$btn_delete_entrance_exam_state.data 'endpoint'
- data: send_data
- success: @clear_errors_then ->
- success_message = gettext("Entrance exam state is being deleted for student '{student_id}'.")
- full_success_message = interpolate_text(success_message, {student_id: unique_student_identifier})
- alert full_success_message
- error: std_ajax_err =>
- error_message = gettext("Error deleting entrance exam state for student '{student_id}'. Make sure student identifier is correct.")
- full_error_message = interpolate_text(error_message, {student_id: unique_student_identifier})
- @$request_response_error_ee.text full_error_message
-
- # list entrance exam task history for student
- @$btn_entrance_exam_task_history.click =>
- unique_student_identifier = @$field_entrance_exam_student_select_grade.val()
- if not unique_student_identifier
- return @$request_response_error_ee.text gettext("Enter a student's username or email address.")
- send_data =
- unique_student_identifier: unique_student_identifier
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$btn_entrance_exam_task_history.data 'endpoint'
- data: send_data
- success: @clear_errors_then (data) =>
- create_task_list_table @$table_entrance_exam_task_history, data.tasks
- error: std_ajax_err =>
- error_message = gettext("Error getting entrance exam task history for student '{student_id}'. Make sure student identifier is correct.")
- full_error_message = interpolate_text(error_message, {student_id: unique_student_identifier})
- @$request_response_error_ee.text full_error_message
-
- # start task to reset attempts on problem for all students
- @$btn_reset_attempts_all.click =>
- problem_to_reset = @$field_problem_select_all.val()
- if not problem_to_reset
- return @$request_response_error_all.text gettext("Please enter a problem location.")
- confirm_message = gettext("Reset attempts for all students on problem '<%= problem_id %>'?")
- full_confirm_message = _.template(confirm_message)({problem_id: problem_to_reset})
- if window.confirm full_confirm_message
- send_data =
- all_students: true
- problem_to_reset: problem_to_reset
- success_message = gettext("Successfully started task to reset attempts for problem '<%= problem_id %>'. Click the 'Show Background Task History for Problem' button to see the status of the task.")
- full_success_message = _.template(success_message)({problem_id: problem_to_reset})
- error_message = gettext("Error starting a task to reset attempts for all students on problem '<%= problem_id %>'. Make sure that the problem identifier is complete and correct.")
- full_error_message = _.template(error_message)({problem_id: problem_to_reset})
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$btn_reset_attempts_all.data 'endpoint'
- data: send_data
- success: @clear_errors_then -> alert full_success_message
- error: std_ajax_err => @$request_response_error_all.text full_error_message
- else
- # Clear error messages if "Cancel" was chosen on confirmation alert
- @clear_errors()
-
- # start task to rescore problem for all students
- @$btn_rescore_problem_all.click =>
- problem_to_reset = @$field_problem_select_all.val()
- if not problem_to_reset
- return @$request_response_error_all.text gettext("Please enter a problem location.")
- confirm_message = gettext("Rescore problem '<%= problem_id %>' for all students?")
- full_confirm_message = _.template(confirm_message)({problem_id: problem_to_reset})
- if window.confirm full_confirm_message
- send_data =
- all_students: true
- problem_to_reset: problem_to_reset
- success_message = gettext("Successfully started task to rescore problem '<%= problem_id %>' for all students. Click the 'Show Background Task History for Problem' button to see the status of the task.")
- full_success_message = _.template(success_message)({problem_id: problem_to_reset})
- error_message = gettext("Error starting a task to rescore problem '<%= problem_id %>'. Make sure that the problem identifier is complete and correct.")
- full_error_message = _.template(error_message)({problem_id: problem_to_reset})
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$btn_rescore_problem_all.data 'endpoint'
- data: send_data
- success: @clear_errors_then -> alert full_success_message
- error: std_ajax_err => @$request_response_error_all.text full_error_message
- else
- # Clear error messages if "Cancel" was chosen on confirmation alert
- @clear_errors()
-
- # list task history for problem
- @$btn_task_history_all.click =>
- send_data =
- problem_location_str: @$field_problem_select_all.val()
-
- if not send_data.problem_location_str
- return @$request_response_error_all.text gettext("Please enter a problem location.")
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$btn_task_history_all.data 'endpoint'
- data: send_data
- success: @clear_errors_then (data) =>
- create_task_list_table @$table_task_history_all, data.tasks
- error: std_ajax_err => @$request_response_error_all.text gettext("Error listing task history for this student and problem.")
-
- # wraps a function, but first clear the error displays
- clear_errors_then: (cb) ->
- @$request_response_error_progress.empty()
- @$request_response_error_grade.empty()
- @$request_response_error_ee.empty()
- @$request_response_error_all.empty()
- ->
- cb?.apply this, arguments
-
-
- clear_errors: ->
- @$request_response_error_progress.empty()
- @$request_response_error_grade.empty()
- @$request_response_error_ee.empty()
- @$request_response_error_all.empty()
-
- # handler for when the section title is clicked.
- onClickTitle: -> @instructor_tasks.task_poller.start()
-
- # handler for when the section is closed
- onExit: -> @instructor_tasks.task_poller.stop()
-
-
-# export for use
-# create parent namespaces if they do not already exist.
-_.defaults window, InstructorDashboard: {}
-_.defaults window.InstructorDashboard, sections: {}
-_.defaults window.InstructorDashboard.sections,
- StudentAdmin: StudentAdmin
diff --git a/lms/static/coffee/src/instructor_dashboard/util.coffee b/lms/static/coffee/src/instructor_dashboard/util.coffee
deleted file mode 100644
index 5782107b22..0000000000
--- a/lms/static/coffee/src/instructor_dashboard/util.coffee
+++ /dev/null
@@ -1,463 +0,0 @@
-# Common utilities for instructor dashboard components.
-
-# reverse arguments on common functions to enable
-# better coffeescript with callbacks at the end.
-plantTimeout = (ms, cb) -> setTimeout cb, ms
-plantInterval = (ms, cb) -> setInterval cb, ms
-
-
-# get jquery element and assert its existance
-find_and_assert = ($root, selector) ->
- item = $root.find selector
- if item.length != 1
- console.error "element selection failed for '#{selector}' resulted in length #{item.length}"
- throw "Failed Element Selection"
- else
- item
-
-# standard ajax error wrapper
-#
-# wraps a `handler` function so that first
-# it prints basic error information to the console.
-@std_ajax_err = (handler) -> (jqXHR, textStatus, errorThrown) ->
- console.warn """ajax error
- textStatus: #{textStatus}
- errorThrown: #{errorThrown}"""
- handler.apply this, arguments
-
-
-# render a task list table to the DOM
-# `$table_tasks` the $element in which to put the table
-# `tasks_data`
-@create_task_list_table = ($table_tasks, tasks_data) ->
- $table_tasks.empty()
-
- options =
- enableCellNavigation: true
- enableColumnReorder: false
- autoHeight: true
- rowHeight: 100
- forceFitColumns: true
-
- columns = [
- id: 'task_type'
- field: 'task_type'
- ###
- Translators: a "Task" is a background process such as grading students or sending email
- ###
- name: gettext('Task Type')
- minWidth: 102
- ,
- id: 'task_input'
- field: 'task_input'
- ###
- Translators: a "Task" is a background process such as grading students or sending email
- ###
- name: gettext('Task inputs')
- minWidth: 150
- ,
- id: 'task_id'
- field: 'task_id'
- ###
- Translators: a "Task" is a background process such as grading students or sending email
- ###
- name: gettext('Task ID')
- minWidth: 150
- ,
- id: 'requester'
- field: 'requester'
- ###
- Translators: a "Requester" is a username that requested a task such as sending email
- ###
- name: gettext('Requester')
- minWidth: 80
- ,
- id: 'created'
- field: 'created'
- ###
- Translators: A timestamp of when a task (eg, sending email) was submitted appears after this
- ###
- name: gettext('Submitted')
- minWidth: 120
- ,
- id: 'duration_sec'
- field: 'duration_sec'
- ###
- Translators: The length of a task (eg, sending email) in seconds appears this
- ###
- name: gettext('Duration (sec)')
- minWidth: 80
- ,
- id: 'task_state'
- field: 'task_state'
- ###
- Translators: The state (eg, "In progress") of a task (eg, sending email) appears after this.
- ###
- name: gettext('State')
- minWidth: 80
- ,
- id: 'status'
- field: 'status'
- ###
- Translators: a "Task" is a background process such as grading students or sending email
- ###
- name: gettext('Task Status')
- minWidth: 80
- ,
- id: 'task_message'
- field: 'task_message'
- ###
- Translators: a "Task" is a background process such as grading students or sending email
- ###
- name: gettext('Task Progress')
- minWidth: 120
- ]
-
- table_data = tasks_data
-
- $table_placeholder = $ '', class: 'slickgrid'
- $table_tasks.append($table_placeholder)
- grid = new Slick.Grid($table_placeholder, table_data, columns, options)
-
-# Formats the subject field for email content history table
-subject_formatter = (row, cell, value, columnDef, dataContext) ->
- if value is null then return gettext("An error occurred retrieving your email. Please try again later, and contact technical support if the problem persists.")
- subject_text = $('').text(value['subject']).html()
- return edx.HtmlUtils.joinHtml(
- edx.HtmlUtils.HTML('