Commit b3ec4d1b by Carson Gee

Switching to new dashboard API and adding rescore to actions list

parent 2998a2e9
...@@ -66,6 +66,15 @@ class StaffDebugPage(PageObject): ...@@ -66,6 +66,15 @@ class StaffDebugPage(PageObject):
self.q(css='input[id^=sd_fu_]').fill(user) self.q(css='input[id^=sd_fu_]').fill(user)
self.q(css='section.staff-modal a#staff-debug-sdelete').click() self.q(css='section.staff-modal a#staff-debug-sdelete').click()
def rescore(self, user=None):
"""
This clicks on the reset attempts link with an optionally
specified user.
"""
if user:
self.q(css='input[id^=sd_fu_]').first.fill(user)
self.q(css='section.staff-modal a#staff-debug-rescore').click()
@property @property
def idash_msg(self): def idash_msg(self):
""" """
......
...@@ -16,6 +16,7 @@ class StaffDebugTest(UniqueCourseTest): ...@@ -16,6 +16,7 @@ class StaffDebugTest(UniqueCourseTest):
Tests that verify the staff debug info. Tests that verify the staff debug info.
""" """
USERNAME = "STAFF_TESTER" USERNAME = "STAFF_TESTER"
EMAIL = "johndoe@example.com"
def setUp(self): def setUp(self):
super(StaffDebugTest, self).setUp() super(StaffDebugTest, self).setUp()
...@@ -49,7 +50,7 @@ class StaffDebugTest(UniqueCourseTest): ...@@ -49,7 +50,7 @@ class StaffDebugTest(UniqueCourseTest):
# Auto-auth register for the course. # Auto-auth register for the course.
# Do this as global staff so that you will see the Staff View # Do this as global staff so that you will see the Staff View
AutoAuthPage(self.browser, username=self.USERNAME, AutoAuthPage(self.browser, username=self.USERNAME, email=self.EMAIL,
course_id=self.course_id, staff=True).visit() course_id=self.course_id, staff=True).visit()
def _goto_staff_page(self): def _goto_staff_page(self):
...@@ -63,15 +64,14 @@ class StaffDebugTest(UniqueCourseTest): ...@@ -63,15 +64,14 @@ class StaffDebugTest(UniqueCourseTest):
def test_reset_attempts_empty(self): def test_reset_attempts_empty(self):
""" """
Test that we fail properly when there is no student state Test that we reset even when there is no student state
""" """
staff_debug_page = self._goto_staff_page().open_staff_debug_info() staff_debug_page = self._goto_staff_page().open_staff_debug_info()
staff_debug_page.reset_attempts() staff_debug_page.reset_attempts()
msg = staff_debug_page.idash_msg[0] msg = staff_debug_page.idash_msg[0]
self.assertIn((u"Found a single student. Found module. Couldn't " self.assertEqual(u'Successfully reset the attempts '
"reset module state for {0}/").format(self.USERNAME), 'for user {}'.format(self.USERNAME), msg)
msg)
def test_delete_state_empty(self): def test_delete_state_empty(self):
""" """
...@@ -80,8 +80,8 @@ class StaffDebugTest(UniqueCourseTest): ...@@ -80,8 +80,8 @@ class StaffDebugTest(UniqueCourseTest):
staff_debug_page = self._goto_staff_page().open_staff_debug_info() staff_debug_page = self._goto_staff_page().open_staff_debug_info()
staff_debug_page.delete_state() staff_debug_page.delete_state()
msg = staff_debug_page.idash_msg[0] msg = staff_debug_page.idash_msg[0]
self.assertIn((u"Found a single student. Found module. " self.assertEqual(u'Successfully deleted student state '
"Deleted student module state for"), msg) 'for user {}'.format(self.USERNAME), msg)
def test_reset_attempts_state(self): def test_reset_attempts_state(self):
""" """
...@@ -93,10 +93,25 @@ class StaffDebugTest(UniqueCourseTest): ...@@ -93,10 +93,25 @@ class StaffDebugTest(UniqueCourseTest):
staff_debug_page = staff_page.open_staff_debug_info() staff_debug_page = staff_page.open_staff_debug_info()
staff_debug_page.reset_attempts() staff_debug_page.reset_attempts()
msg = staff_debug_page.idash_msg[0] msg = staff_debug_page.idash_msg[0]
self.assertIn((u"Found a single student. Found module. Module " self.assertEqual(u'Successfully reset the attempts '
"state successfully reset!"), msg) 'for user {}'.format(self.USERNAME), msg)
def test_student_state_state(self): def test_rescore_state(self):
"""
Rescore the student
"""
staff_page = self._goto_staff_page()
staff_page.answer_problem()
staff_debug_page = staff_page.open_staff_debug_info()
staff_debug_page.rescore()
msg = staff_debug_page.idash_msg[0]
# Since we aren't running celery stuff, this will fail badly
# for now, but is worth excercising that bad of a response
self.assertEqual(u'Unsuccessfully rescored problem. '
'Unknown Error Occurred.', msg)
def test_student_state_delete(self):
""" """
Successfully delete the student state with an answer Successfully delete the student state with an answer
""" """
...@@ -106,5 +121,31 @@ class StaffDebugTest(UniqueCourseTest): ...@@ -106,5 +121,31 @@ class StaffDebugTest(UniqueCourseTest):
staff_debug_page = staff_page.open_staff_debug_info() staff_debug_page = staff_page.open_staff_debug_info()
staff_debug_page.delete_state() staff_debug_page.delete_state()
msg = staff_debug_page.idash_msg[0] msg = staff_debug_page.idash_msg[0]
self.assertIn((u"Found a single student. Found module. " self.assertEqual(u'Successfully deleted student state '
"Deleted student module state for"), msg) 'for user {}'.format(self.USERNAME), msg)
def test_student_by_email(self):
"""
Successfully reset the student attempts using their email address
"""
staff_page = self._goto_staff_page()
staff_page.answer_problem()
staff_debug_page = staff_page.open_staff_debug_info()
staff_debug_page.reset_attempts(self.EMAIL)
msg = staff_debug_page.idash_msg[0]
self.assertEqual(u'Successfully reset the attempts '
'for user {}'.format(self.EMAIL), msg)
def test_bad_student(self):
"""
Test negative response with invalid user
"""
staff_page = self._goto_staff_page()
staff_page.answer_problem()
staff_debug_page = staff_page.open_staff_debug_info()
staff_debug_page.delete_state('INVALIDUSER')
msg = staff_debug_page.idash_msg[0]
self.assertEqual(u'Unsuccessfully deleted student state. '
'User does not exist.', msg)
...@@ -6,7 +6,7 @@ describe('StaffDebugActions', function() { ...@@ -6,7 +6,7 @@ describe('StaffDebugActions', function() {
describe('get_url ', function() { describe('get_url ', function() {
it('defines url to courseware ajax entry point', function() { it('defines url to courseware ajax entry point', function() {
spyOn(StaffDebug, "get_current_url").andReturn("/courses/edX/Open_DemoX/edx_demo_course/courseware/stuff"); spyOn(StaffDebug, "get_current_url").andReturn("/courses/edX/Open_DemoX/edx_demo_course/courseware/stuff");
expect(StaffDebug.get_url('instructor')).toBe('/courses/edX/Open_DemoX/edx_demo_course/instructor'); expect(StaffDebug.get_url('rescore_problem')).toBe('/courses/edX/Open_DemoX/edx_demo_course/instructor_dashboard/api/rescore_problem');
}); });
}); });
...@@ -26,21 +26,22 @@ describe('StaffDebugActions', function() { ...@@ -26,21 +26,22 @@ describe('StaffDebugActions', function() {
$('#' + fixture_id).remove(); $('#' + fixture_id).remove();
}); });
}); });
describe('reset', function() { describe('reset', function() {
it('makes an ajax call with the expected parameters', function() { it('makes an ajax call with the expected parameters', function() {
$('body').append(fixture); $('body').append(fixture);
spyOn($, 'ajax'); spyOn($, 'ajax');
StaffDebug.reset(loc) StaffDebug.reset(loc);
expect($.ajax.mostRecentCall.args[0]['type']).toEqual('POST'); expect($.ajax.mostRecentCall.args[0]['type']).toEqual('GET');
expect($.ajax.mostRecentCall.args[0]['data']).toEqual({ expect($.ajax.mostRecentCall.args[0]['data']).toEqual({
'action': "Reset student's attempts", 'problem_to_reset': loc,
'problem_for_student': loc, 'unique_student_identifier': 'userman',
'unique_student_identifier': 'userman' 'delete_module': false
}); });
expect($.ajax.mostRecentCall.args[0]['url']).toEqual('/instructor'); expect($.ajax.mostRecentCall.args[0]['url']).toEqual(
'/instructor_dashboard/api/reset_student_attempts'
);
$('#' + fixture_id).remove(); $('#' + fixture_id).remove();
}); });
}); });
...@@ -49,17 +50,40 @@ describe('StaffDebugActions', function() { ...@@ -49,17 +50,40 @@ describe('StaffDebugActions', function() {
$('body').append(fixture); $('body').append(fixture);
spyOn($, 'ajax'); spyOn($, 'ajax');
StaffDebug.sdelete(loc) StaffDebug.sdelete(loc);
expect($.ajax.mostRecentCall.args[0]['type']).toEqual('POST'); expect($.ajax.mostRecentCall.args[0]['type']).toEqual('GET');
expect($.ajax.mostRecentCall.args[0]['data']).toEqual({ expect($.ajax.mostRecentCall.args[0]['data']).toEqual({
'action': "Delete student state for module", 'problem_to_reset': loc,
'problem_for_student': loc, 'unique_student_identifier': 'userman',
'unique_student_identifier': 'userman' 'delete_module': true
}); });
expect($.ajax.mostRecentCall.args[0]['url']).toEqual('/instructor'); expect($.ajax.mostRecentCall.args[0]['url']).toEqual(
'/instructor_dashboard/api/reset_student_attempts'
);
$('#' + fixture_id).remove(); $('#' + fixture_id).remove();
}); });
}); });
describe('rescore', function() {
it('makes an ajax call with the expected parameters', function() {
$('body').append(fixture);
spyOn($, 'ajax');
StaffDebug.rescore(loc);
expect($.ajax.mostRecentCall.args[0]['type']).toEqual('GET');
expect($.ajax.mostRecentCall.args[0]['data']).toEqual({
'problem_to_reset': loc,
'unique_student_identifier': 'userman',
'delete_module': false
});
expect($.ajax.mostRecentCall.args[0]['url']).toEqual(
'/instructor_dashboard/api/rescore_problem'
);
$('#' + fixture_id).remove();
});
});
}); });
...@@ -7,7 +7,7 @@ var StaffDebug = (function(){ ...@@ -7,7 +7,7 @@ var StaffDebug = (function(){
get_url = function(action){ get_url = function(action){
var pathname = this.get_current_url(); var pathname = this.get_current_url();
var url = pathname.substr(0,pathname.indexOf('/courseware')) + '/' + action; var url = pathname.substr(0,pathname.indexOf('/courseware')) + '/instructor_dashboard/api/' + action;
return url; return url;
} }
...@@ -19,50 +19,89 @@ var StaffDebug = (function(){ ...@@ -19,50 +19,89 @@ var StaffDebug = (function(){
return uname; return uname;
} }
do_idash_action = function(locname, idaction){ do_idash_action = function(action){
var pdata = { var pdata = {
'action': idaction, 'problem_to_reset': action.location,
'problem_for_student': locname, 'unique_student_identifier': get_user(action.location),
'unique_student_identifier': get_user(locname) 'delete_module': action.delete_module
} }
$.ajax({ $.ajax({
type: "POST", type: "GET",
url: get_url('instructor'), url: get_url(action.method),
data: pdata, data: pdata,
success: function(data){ success: function(data){
var msg = $("#idash_msg", data); var text = _.template(
$("#result_" + locname).html( msg ); gettext('Successfully {action} for user {user}'),
{
action: action.description,
user: data.student
},
{interpolate: /\{(.+?)\}/g}
)
var html = _.template(
'<p id="idash_msg" class="success">{text}</p>',
{text: text},
{interpolate: /\{(.+?)\}/g}
)
$("#result_"+action.location).html(html);
}, },
error: function(request, status, error) { error: function(request, status, error) {
var response_json;
try {
response_json = $.parseJSON(request.responseText);
} catch(e) {
response_json = { error: gettext('Unknown Error Occurred.') };
}
var text = _.template( var text = _.template(
gettext('Something has gone wrong with this request. \ gettext('Unsuccessfully {action}. {error}'),
The server replied with a status of: {error}'), {
{error: error}, error: response_json.error,
action: action.description
},
{interpolate: /\{(.+?)\}/g} {interpolate: /\{(.+?)\}/g}
) )
var html = _.template( var html = _.template(
'<p id="idash_msg"><font color="red">{text}</font></p>', '<p id="idash_msg" class="error">{text}</p>',
{text: text}, {text: text},
{interpolate: /\{(.+?)\}/g} {interpolate: /\{(.+?)\}/g}
) )
$("#result_"+locname).html(html); $("#result_"+action.location).html(html);
}, },
dataType: 'html' dataType: 'json'
}); });
} }
reset = function(locname){ reset = function(locname){
do_idash_action(locname, "Reset student's attempts"); this.do_idash_action({
location: locname,
method: 'reset_student_attempts',
description: gettext('reset the attempts'),
delete_module: false
});
} }
sdelete = function(locname){ sdelete = function(locname){
do_idash_action(locname, "Delete student state for module"); this.do_idash_action({
location: locname,
method: 'reset_student_attempts',
description: gettext('deleted student state'),
delete_module: true
});
}
rescore = function(locname){
this.do_idash_action({
location: locname,
method: 'rescore_problem',
description: gettext('rescored problem'),
delete_module: false
});
} }
return { return {
reset: reset, reset: reset,
sdelete: sdelete, sdelete: sdelete,
rescore: rescore,
do_idash_action: do_idash_action, do_idash_action: do_idash_action,
get_current_url: get_current_url, get_current_url: get_current_url,
get_url: get_url, get_url: get_url,
...@@ -80,4 +119,8 @@ $(document).ready(function() { ...@@ -80,4 +119,8 @@ $(document).ready(function() {
StaffDebug.sdelete($(this).data('location')); StaffDebug.sdelete($(this).data('location'));
return false; return false;
}); });
$('#staff-debug-rescore').click(function() {
StaffDebug.rescore($(this).data('location'));
return false;
});
}); });
...@@ -191,6 +191,15 @@ div.course-wrapper { ...@@ -191,6 +191,15 @@ div.course-wrapper {
} }
} }
div.staff_actions {
p.error {
color: $error-red
}
p.success {
color: $success-color;
}
}
div.staff_info { div.staff_info {
display: none; display: none;
@include clearfix(); @include clearfix();
......
...@@ -54,20 +54,25 @@ ${block_content} ...@@ -54,20 +54,25 @@ ${block_content}
<h2>${_('Staff Debug')}</h2> <h2>${_('Staff Debug')}</h2>
</header> </header>
<hr /> <hr />
<h3>${_('Actions')}</h3> <div class="staff_actions">
<div> <h3>${_('Actions')}</h3>
<label for="sd_fu_${location.name}">${_('For User')}:</label> <div>
<input type="text" id="sd_fu_${location.name}" placeholder="${user.username}"/> <label for="sd_fu_${location.name}">${_('Username')}:</label>
</div> <input type="text" id="sd_fu_${location.name}" placeholder="${user.username}"/>
<div> </div>
[ <div>
<a href="#" id="staff-debug-reset" data-location="${location.name}">${_('Reset Student Attempts')}</a> [
| <a href="#" id="staff-debug-reset" data-location="${location.name}">${_('Reset Student Attempts')}</a>
<a href="#" id="staff-debug-sdelete" data-location="${location.name}">${_('Delete Student State')}</a> |
] <a href="#" id="staff-debug-sdelete" data-location="${location.name}">${_('Delete Student State')}</a>
</div> |
<div id="result_${location.name}"/> <a href="#" id="staff-debug-rescore" data-location="${location.name}">${_('Rescore Student Submission')}</a>
]
</div>
<div id="result_${location.name}"/>
</div>
<div class="staff_info" style="display:block"> <div class="staff_info" style="display:block">
is_released = ${is_released} is_released = ${is_released}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment