Merge pull request #15045 from edx/neem/handle_unrescorable
400 for non-rescorable blocks
This commit is contained in:
@@ -101,6 +101,11 @@ class InstructorDashboardPage(CoursePage):
|
||||
ecommerce_section.wait_for_page()
|
||||
return ecommerce_section
|
||||
|
||||
def is_rescore_unsupported_message_visible(self):
|
||||
return u'This component cannot be rescored.' in unicode(
|
||||
self.q(css='.request-response-error').html
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_asset_path(file_name):
|
||||
"""
|
||||
|
||||
@@ -15,14 +15,18 @@ from common.test.acceptance.pages.lms.auto_auth import AutoAuthPage
|
||||
from common.test.acceptance.pages.studio.overview import CourseOutlinePage as StudioCourseOutlinePage
|
||||
from common.test.acceptance.pages.lms.create_mode import ModeCreationPage
|
||||
from common.test.acceptance.pages.lms.courseware import CoursewarePage
|
||||
from common.test.acceptance.pages.lms.instructor_dashboard import InstructorDashboardPage, EntranceExamAdmin
|
||||
from common.test.acceptance.pages.lms.instructor_dashboard import (
|
||||
InstructorDashboardPage,
|
||||
EntranceExamAdmin,
|
||||
StudentSpecificAdmin,
|
||||
)
|
||||
from common.test.acceptance.fixtures.course import CourseFixture, XBlockFixtureDesc
|
||||
from common.test.acceptance.pages.lms.dashboard import DashboardPage
|
||||
from common.test.acceptance.pages.lms.problem import ProblemPage
|
||||
from common.test.acceptance.pages.lms.pay_and_verify import PaymentAndVerificationFlow
|
||||
from common.test.acceptance.pages.lms.login_and_register import CombinedLoginAndRegisterPage
|
||||
from common.test.acceptance.pages.common.utils import enroll_user_track
|
||||
from common.test.acceptance.tests.helpers import disable_animations
|
||||
from common.test.acceptance.tests.helpers import disable_animations, create_multiple_choice_problem
|
||||
from common.test.acceptance.fixtures.certificates import CertificateConfigFixture
|
||||
|
||||
|
||||
@@ -1337,3 +1341,50 @@ class EcommerceTest(BaseInstructorDashboardTest):
|
||||
|
||||
# Log in and visit E-commerce section under Instructor dashboard
|
||||
self.assertNotIn(u'Coupon Code List', self.visit_ecommerce_section().get_sections_header_values())
|
||||
|
||||
|
||||
class StudentAdminTest(BaseInstructorDashboardTest):
|
||||
SECTION_NAME = 'Test Section 1'
|
||||
SUBSECTION_NAME = 'Test Subsection 1'
|
||||
UNIT_NAME = 'Test Unit 1'
|
||||
PROBLEM_NAME = 'Test Problem 1'
|
||||
|
||||
def setUp(self):
|
||||
super(StudentAdminTest, self).setUp()
|
||||
self.course_fix = CourseFixture(
|
||||
self.course_info['org'],
|
||||
self.course_info['number'],
|
||||
self.course_info['run'],
|
||||
self.course_info['display_name']
|
||||
)
|
||||
|
||||
self.problem = create_multiple_choice_problem(self.PROBLEM_NAME)
|
||||
self.vertical = XBlockFixtureDesc('vertical', "Lab Unit")
|
||||
self.course_fix.add_children(
|
||||
XBlockFixtureDesc('chapter', self.SECTION_NAME).add_children(
|
||||
XBlockFixtureDesc('sequential', self.SUBSECTION_NAME).add_children(
|
||||
self.vertical.add_children(self.problem)
|
||||
)
|
||||
),
|
||||
).install()
|
||||
|
||||
self.username, _ = self.log_in_as_instructor()
|
||||
self.instructor_dashboard_page = self.visit_instructor_dashboard()
|
||||
|
||||
def test_rescore_nonrescorable(self):
|
||||
student_admin_section = self.instructor_dashboard_page.select_student_admin(StudentSpecificAdmin)
|
||||
student_admin_section.set_student_email_or_username(self.username)
|
||||
|
||||
# not a rescorable block
|
||||
student_admin_section.set_problem_location(self.vertical.locator)
|
||||
getattr(student_admin_section, 'rescore_button').click()
|
||||
self.assertTrue(self.instructor_dashboard_page.is_rescore_unsupported_message_visible())
|
||||
|
||||
def test_rescore_rescorable(self):
|
||||
student_admin_section = self.instructor_dashboard_page.select_student_admin(StudentSpecificAdmin)
|
||||
student_admin_section.set_student_email_or_username(self.username)
|
||||
student_admin_section.set_problem_location(self.problem.locator)
|
||||
getattr(student_admin_section, 'rescore_button').click()
|
||||
alert = get_modal_alert(student_admin_section.browser)
|
||||
alert.dismiss()
|
||||
self.assertFalse(self.instructor_dashboard_page.is_rescore_unsupported_message_visible())
|
||||
|
||||
@@ -2118,18 +2118,24 @@ def rescore_problem(request, course_id):
|
||||
|
||||
if student:
|
||||
response_payload['student'] = student_identifier
|
||||
lms.djangoapps.instructor_task.api.submit_rescore_problem_for_student(
|
||||
request,
|
||||
module_state_key,
|
||||
student,
|
||||
only_if_higher,
|
||||
)
|
||||
try:
|
||||
lms.djangoapps.instructor_task.api.submit_rescore_problem_for_student(
|
||||
request,
|
||||
module_state_key,
|
||||
student,
|
||||
only_if_higher,
|
||||
)
|
||||
except NotImplementedError as exc:
|
||||
return HttpResponseBadRequest(exc.message)
|
||||
elif all_students:
|
||||
lms.djangoapps.instructor_task.api.submit_rescore_problem_for_all_students(
|
||||
request,
|
||||
module_state_key,
|
||||
only_if_higher,
|
||||
)
|
||||
try:
|
||||
lms.djangoapps.instructor_task.api.submit_rescore_problem_for_all_students(
|
||||
request,
|
||||
module_state_key,
|
||||
only_if_higher,
|
||||
)
|
||||
except NotImplementedError as exc:
|
||||
return HttpResponseBadRequest(exc.message)
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
|
||||
@@ -255,10 +255,14 @@ def check_arguments_for_rescoring(usage_key):
|
||||
in). An ItemNotFoundException is raised if the corresponding module
|
||||
descriptor doesn't exist. NotImplementedError is raised if the
|
||||
corresponding module doesn't support rescoring calls.
|
||||
|
||||
Note: the string returned here is surfaced as the error
|
||||
message on the instructor dashboard when a rescore is
|
||||
submitted for a non-rescorable block.
|
||||
"""
|
||||
descriptor = modulestore().get_item(usage_key)
|
||||
if not _supports_rescore(descriptor):
|
||||
msg = "Specified module does not support rescoring."
|
||||
msg = _("This component cannot be rescored.")
|
||||
raise NotImplementedError(msg)
|
||||
|
||||
|
||||
|
||||
@@ -436,7 +436,7 @@
|
||||
}
|
||||
|
||||
StudentAdmin.prototype.rescore_problem_single = function(onlyIfHigher) {
|
||||
var errorMessage, fullErrorMessage, fullSuccessMessage,
|
||||
var defaultErrorMessage, fullDefaultErrorMessage, fullSuccessMessage,
|
||||
problemToReset, sendData, successMessage, uniqStudentIdentifier,
|
||||
that = this;
|
||||
uniqStudentIdentifier = this.$field_student_select_grade.val();
|
||||
@@ -461,8 +461,8 @@
|
||||
student_id: uniqStudentIdentifier,
|
||||
problem_id: problemToReset
|
||||
});
|
||||
errorMessage = 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."); // eslint-disable-line max-len
|
||||
fullErrorMessage = _.template(errorMessage)({
|
||||
defaultErrorMessage = 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."); // eslint-disable-line max-len
|
||||
fullDefaultErrorMessage = _.template(defaultErrorMessage)({
|
||||
student_id: uniqStudentIdentifier,
|
||||
problem_id: problemToReset
|
||||
});
|
||||
@@ -474,8 +474,11 @@
|
||||
success: this.clear_errors_then(function() {
|
||||
return alert(fullSuccessMessage); // eslint-disable-line no-alert
|
||||
}),
|
||||
error: statusAjaxError(function() {
|
||||
return that.$request_err_grade.text(fullErrorMessage);
|
||||
error: statusAjaxError(function(response) {
|
||||
if (response.responseText) {
|
||||
return that.$request_err_grade.text(response.responseText);
|
||||
}
|
||||
return that.$request_err_grade.text(fullDefaultErrorMessage);
|
||||
})
|
||||
});
|
||||
};
|
||||
@@ -518,8 +521,9 @@
|
||||
};
|
||||
|
||||
StudentAdmin.prototype.rescore_problem_all = function(onlyIfHigher) {
|
||||
var confirmMessage, errorMessage, fullConfirmMessage,
|
||||
fullErrorMessage, fullSuccessMessage, problemToReset, sendData, successMessage,
|
||||
var confirmMessage, defaultErrorMessage, fullConfirmMessage,
|
||||
fullDefaultErrorMessage, fullSuccessMessage, problemToReset,
|
||||
sendData, successMessage,
|
||||
that = this;
|
||||
problemToReset = this.$field_problem_select_all.val();
|
||||
if (!problemToReset) {
|
||||
@@ -541,8 +545,8 @@
|
||||
fullSuccessMessage = _.template(successMessage)({
|
||||
problem_id: problemToReset
|
||||
});
|
||||
errorMessage = gettext("Error starting a task to rescore problem '<%- problem_id %>'. Make sure that the problem identifier is complete and correct."); // eslint-disable-line max-len
|
||||
fullErrorMessage = _.template(errorMessage)({
|
||||
defaultErrorMessage = gettext("Error starting a task to rescore problem '<%- problem_id %>'. Make sure that the problem identifier is complete and correct."); // eslint-disable-line max-len
|
||||
fullDefaultErrorMessage = _.template(defaultErrorMessage)({
|
||||
problem_id: problemToReset
|
||||
});
|
||||
return $.ajax({
|
||||
@@ -553,8 +557,11 @@
|
||||
success: this.clear_errors_then(function() {
|
||||
return alert(fullSuccessMessage); // eslint-disable-line no-alert
|
||||
}),
|
||||
error: statusAjaxError(function() {
|
||||
return that.$request_response_error_all.text(fullErrorMessage);
|
||||
error: statusAjaxError(function(response) {
|
||||
if (response.responseText) {
|
||||
return that.$request_response_error_all.text(response.responseText);
|
||||
}
|
||||
return that.$request_response_error_all.text(fullDefaultErrorMessage);
|
||||
})
|
||||
});
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user