Commit e3fe2cbf by muzaffaryousaf

Changing the student anonymous_user_id to student name.

TNL-836
parent d11f3139
...@@ -129,15 +129,15 @@ ...@@ -129,15 +129,15 @@
<form id="openassessment_student_info_form"> <form id="openassessment_student_info_form">
<ul> <ul>
<li class="openassessment__student-info_list"> <li class="openassessment__student-info_list">
<label for="openassessment__student_id" class="label">{% trans "Get Student Info" %}</label> <label for="openassessment__student_username" class="label">{% trans "Get Student Info" %}</label>
</li> </li>
<li class="openassessment__student-info_list"> <li class="openassessment__student-info_list">
<input id="openassessment__student_id" type="text" class="value" maxlength="255"> <input id="openassessment__student_username" type="text" class="value" maxlength="255">
</li> </li>
</ul> </ul>
<ul class="list list--actions"> <ul class="list list--actions">
<li class="list--actions__item"> <li class="list--actions__item">
<a aria-role="button" href="" id="submit_student_id" class="action--submit"><span class="copy">{% trans "Submit" %}</span></a> <a aria-role="button" href="" id="submit_student_username" class="action--submit"><span class="copy">{% trans "Submit" %}</span></a>
</li> </li>
</ul> </ul>
</form> </form>
......
...@@ -90,6 +90,7 @@ def load(path): ...@@ -90,6 +90,7 @@ def load(path):
return data.decode("utf8") return data.decode("utf8")
@XBlock.needs("i18n") @XBlock.needs("i18n")
@XBlock.needs("user")
class OpenAssessmentBlock( class OpenAssessmentBlock(
MessageMixin, MessageMixin,
SubmissionMixin, SubmissionMixin,
...@@ -200,22 +201,45 @@ class OpenAssessmentBlock( ...@@ -200,22 +201,45 @@ class OpenAssessmentBlock(
help="Indicates whether or not there are peers to grade." help="Indicates whether or not there are peers to grade."
) )
def get_student_item_dict(self): @property
def course_id(self):
return self._serialize_opaque_key(self.xmodule_runtime.course_id) # pylint:disable=E1101
def get_anonymous_user_id(self, username, course_id):
"""
Get the anonymous user id from Xblock user service.
Args:
username(str): user's name entered by staff to get info.
course_id(str): course id.
Returns:
A unique id for (user, course) pair
"""
return self.runtime.service(self, 'user').get_anonymous_user_id(username, course_id)
def get_student_item_dict(self, anonymous_user_id=None):
"""Create a student_item_dict from our surrounding context. """Create a student_item_dict from our surrounding context.
See also: submissions.api for details. See also: submissions.api for details.
Args:
anonymous_user_id(str):
Returns: Returns:
(dict): The student item associated with this XBlock instance. This (dict): The student item associated with this XBlock instance. This
includes the student id, item id, and course id. includes the student id, item id, and course id.
""" """
item_id = self._serialize_opaque_key(self.scope_ids.usage_id) item_id = self._serialize_opaque_key(self.scope_ids.usage_id)
# This is not the real way course_ids should work, but this is a # This is not the real way course_ids should work, but this is a
# temporary expediency for LMS integration # temporary expediency for LMS integration
if hasattr(self, "xmodule_runtime"): if hasattr(self, "xmodule_runtime"):
course_id = self._serialize_opaque_key(self.xmodule_runtime.course_id) # pylint:disable=E1101 course_id = self.course_id # pylint:disable=E1101
student_id = self.xmodule_runtime.anonymous_student_id # pylint:disable=E1101 if anonymous_user_id:
student_id = anonymous_user_id
else:
student_id = self.xmodule_runtime.anonymous_student_id # pylint:disable=E1101
else: else:
course_id = "edX/Enchantment_101/April_1" course_id = "edX/Enchantment_101/April_1"
if self.scope_ids.user_id is None: if self.scope_ids.user_id is None:
......
...@@ -208,58 +208,59 @@ class StaffInfoMixin(object): ...@@ -208,58 +208,59 @@ class StaffInfoMixin(object):
""" """
Renders all relative information for a specific student's workflow. Renders all relative information for a specific student's workflow.
Given a student's ID, we can render a staff-only section of the page Given a student's username, we can render a staff-only section of the page
with submissions and assessments specific to the student. with submissions and assessments specific to the student.
Must be course staff to render this view. Must be course staff to render this view.
""" """
try: try:
student_id = data.params.get('student_id', '') student_username = data.params.get('student_username', '')
path, context = self.get_student_info_path_and_context(student_id) path, context = self.get_student_info_path_and_context(student_username)
return self.render_assessment(path, context) return self.render_assessment(path, context)
except PeerAssessmentInternalError: except PeerAssessmentInternalError:
return self.render_error(self._(u"Error finding assessment workflow cancellation.")) return self.render_error(self._(u"Error finding assessment workflow cancellation."))
def get_student_info_path_and_context(self, student_id): def get_student_info_path_and_context(self, student_username):
""" """
Get the proper path and context for rendering the the student info Get the proper path and context for rendering the the student info
section of the staff debug panel. section of the staff debug panel.
Args: Args:
student_id (unicode): The ID of the student to report. student_username (unicode): The username of the student to report.
""" """
submission_uuid = None submission_uuid = None
submission = None submission = None
assessment_steps = self.assessment_steps assessment_steps = self.assessment_steps
if student_id: if student_username:
student_item = self.get_student_item_dict() anonymous_user_id = self.get_anonymous_user_id(student_username, self.course_id)
student_item['student_id'] = student_id student_item = self.get_student_item_dict(anonymous_user_id=anonymous_user_id)
# If there is a submission available for the requested student, present if anonymous_user_id:
# it. If not, there will be no other information to collect. # If there is a submission available for the requested student, present
submissions = submission_api.get_submissions(student_item, 1) # it. If not, there will be no other information to collect.
submissions = submission_api.get_submissions(student_item, 1)
if submissions:
submission_uuid = submissions[0]['uuid'] if submissions:
submission = submissions[0] submission_uuid = submissions[0]['uuid']
submission = submissions[0]
if 'file_key' in submission.get('answer', {}):
file_key = submission['answer']['file_key'] if 'file_key' in submission.get('answer', {}):
file_key = submission['answer']['file_key']
try:
submission['image_url'] = file_api.get_download_url(file_key) try:
except file_api.FileUploadError: submission['image_url'] = file_api.get_download_url(file_key)
# Log the error, but do not prevent the rest of the student info except file_api.FileUploadError:
# from being displayed. # Log the error, but do not prevent the rest of the student info
msg = ( # from being displayed.
u"Could not retrieve image URL for staff debug page. " msg = (
u"The student ID is '{student_id}', and the file key is {file_key}" u"Could not retrieve image URL for staff debug page. "
).format(student_id=student_id, file_key=file_key) u"The student username is '{student_username}', and the file key is {file_key}"
logger.exception(msg) ).format(student_username=student_username, file_key=file_key)
logger.exception(msg)
example_based_assessment = None example_based_assessment = None
self_assessment = None self_assessment = None
......
...@@ -48,8 +48,8 @@ OpenAssessment.StaffInfoView.prototype = { ...@@ -48,8 +48,8 @@ OpenAssessment.StaffInfoView.prototype = {
loadStudentInfo: function() { loadStudentInfo: function() {
var view = this; var view = this;
var sel = $('#openassessment__staff-info', this.element); var sel = $('#openassessment__staff-info', this.element);
var student_id = sel.find('#openassessment__student_id').val(); var student_username = sel.find('#openassessment__student_username').val();
this.server.studentInfo(student_id).done( this.server.studentInfo(student_username).done(
function(html) { function(html) {
// Load the HTML and install event handlers // Load the HTML and install event handlers
$('#openassessment__student-info', view.element).replaceWith(html); $('#openassessment__student-info', view.element).replaceWith(html);
...@@ -96,7 +96,7 @@ OpenAssessment.StaffInfoView.prototype = { ...@@ -96,7 +96,7 @@ OpenAssessment.StaffInfoView.prototype = {
); );
// Install a click handler for requesting student info // Install a click handler for requesting student info
sel.find('#submit_student_id').click( sel.find('#submit_student_username').click(
function(eventObject) { function(eventObject) {
eventObject.preventDefault(); eventObject.preventDefault();
view.loadStudentInfo(); view.loadStudentInfo();
......
...@@ -121,14 +121,14 @@ if (typeof OpenAssessment.Server == "undefined" || !OpenAssessment.Server) { ...@@ -121,14 +121,14 @@ if (typeof OpenAssessment.Server == "undefined" || !OpenAssessment.Server) {
/** /**
Load the Student Info section in Staff Info. Load the Student Info section in Staff Info.
**/ **/
studentInfo: function(student_id) { studentInfo: function(student_username) {
var url = this.url('render_student_info'); var url = this.url('render_student_info');
return $.Deferred(function(defer) { return $.Deferred(function(defer) {
$.ajax({ $.ajax({
url: url, url: url,
type: "POST", type: "POST",
dataType: "html", dataType: "html",
data: {student_id: student_id} data: {student_username: student_username}
}).done(function(data) { }).done(function(data) {
defer.resolveWith(this, [data]); defer.resolveWith(this, [data]);
}).fail(function(data) { }).fail(function(data) {
......
...@@ -5,6 +5,8 @@ import datetime ...@@ -5,6 +5,8 @@ import datetime
import urllib import urllib
from mock import Mock, patch from mock import Mock, patch
from django.test.utils import override_settings from django.test.utils import override_settings
import ddt
from openassessment.assessment.api import peer as peer_api from openassessment.assessment.api import peer as peer_api
from openassessment.assessment.api import self as self_api from openassessment.assessment.api import self as self_api
from openassessment.assessment.api import ai as ai_api from openassessment.assessment.api import ai as ai_api
...@@ -13,6 +15,7 @@ from openassessment.assessment.errors.ai import AIError, AIGradingInternalError ...@@ -13,6 +15,7 @@ from openassessment.assessment.errors.ai import AIError, AIGradingInternalError
from openassessment.fileupload.api import FileUploadInternalError from openassessment.fileupload.api import FileUploadInternalError
from submissions import api as sub_api from submissions import api as sub_api
from openassessment.xblock.test.base import scenario, XBlockHandlerTestCase from openassessment.xblock.test.base import scenario, XBlockHandlerTestCase
from xblock.core import XBlock
ALGORITHM_ID = 'fake' ALGORITHM_ID = 'fake'
...@@ -43,6 +46,14 @@ ASSESSMENT_DICT = { ...@@ -43,6 +46,14 @@ ASSESSMENT_DICT = {
} }
class NullUserService(object):
"""
A simple implementation of the runtime "user" service.
"""
def get_anonymous_user_id(self, username, course_id):
return username
class TestCourseStaff(XBlockHandlerTestCase): class TestCourseStaff(XBlockHandlerTestCase):
""" """
Tests for course staff debug panel. Tests for course staff debug panel.
...@@ -66,7 +77,7 @@ class TestCourseStaff(XBlockHandlerTestCase): ...@@ -66,7 +77,7 @@ class TestCourseStaff(XBlockHandlerTestCase):
@scenario('data/basic_scenario.xml', user_id='Bob') @scenario('data/basic_scenario.xml', user_id='Bob')
def test_course_staff_debug_info(self, xblock): def test_course_staff_debug_info(self, xblock):
# If we're not course staff, we shouldn't see the debug info # If we're not course staff, we shouldn't see the debug info
xblock.xmodule_runtime = self._create_mock_runtime( xblock.xmodule_runtime = self._create_mock_runtime(
xblock.scope_ids.usage_id, False, False, "Bob" xblock.scope_ids.usage_id, False, False, "Bob"
) )
resp = self.request(xblock, 'render_staff_info', json.dumps({})) resp = self.request(xblock, 'render_staff_info', json.dumps({}))
...@@ -80,7 +91,7 @@ class TestCourseStaff(XBlockHandlerTestCase): ...@@ -80,7 +91,7 @@ class TestCourseStaff(XBlockHandlerTestCase):
@scenario('data/basic_scenario.xml', user_id='Bob') @scenario('data/basic_scenario.xml', user_id='Bob')
def test_course_student_debug_info(self, xblock): def test_course_student_debug_info(self, xblock):
# If we're not course staff, we shouldn't see the debug info # If we're not course staff, we shouldn't see the debug info
xblock.xmodule_runtime = self._create_mock_runtime( xblock.xmodule_runtime = self._create_mock_runtime(
xblock.scope_ids.usage_id, False, False, "Bob" xblock.scope_ids.usage_id, False, False, "Bob"
) )
resp = self.request(xblock, 'render_student_info', json.dumps({})) resp = self.request(xblock, 'render_student_info', json.dumps({}))
...@@ -96,7 +107,7 @@ class TestCourseStaff(XBlockHandlerTestCase): ...@@ -96,7 +107,7 @@ class TestCourseStaff(XBlockHandlerTestCase):
# If we are in Studio preview mode, don't show the staff debug info # If we are in Studio preview mode, don't show the staff debug info
# In this case, the runtime will tell us that we're staff, # In this case, the runtime will tell us that we're staff,
# but no user ID will be set. # but no user ID will be set.
xblock.xmodule_runtime = self._create_mock_runtime( xblock.xmodule_runtime = self._create_mock_runtime(
xblock.scope_ids.usage_id, True, False, "Bob" xblock.scope_ids.usage_id, True, False, "Bob"
) )
...@@ -112,7 +123,7 @@ class TestCourseStaff(XBlockHandlerTestCase): ...@@ -112,7 +123,7 @@ class TestCourseStaff(XBlockHandlerTestCase):
@scenario('data/staff_dates_scenario.xml', user_id='Bob') @scenario('data/staff_dates_scenario.xml', user_id='Bob')
def test_staff_debug_dates_table(self, xblock): def test_staff_debug_dates_table(self, xblock):
# Simulate that we are course staff # Simulate that we are course staff
xblock.xmodule_runtime = self._create_mock_runtime( xblock.xmodule_runtime = self._create_mock_runtime(
xblock.scope_ids.usage_id, True, False, "Bob" xblock.scope_ids.usage_id, True, False, "Bob"
) )
...@@ -137,7 +148,7 @@ class TestCourseStaff(XBlockHandlerTestCase): ...@@ -137,7 +148,7 @@ class TestCourseStaff(XBlockHandlerTestCase):
@scenario('data/basic_scenario.xml', user_id='Bob') @scenario('data/basic_scenario.xml', user_id='Bob')
def test_staff_debug_dates_distant_past_and_future(self, xblock): def test_staff_debug_dates_distant_past_and_future(self, xblock):
# Simulate that we are course staff # Simulate that we are course staff
xblock.xmodule_runtime = self._create_mock_runtime( xblock.xmodule_runtime = self._create_mock_runtime(
xblock.scope_ids.usage_id, True, False, "Bob" xblock.scope_ids.usage_id, True, False, "Bob"
) )
...@@ -149,7 +160,7 @@ class TestCourseStaff(XBlockHandlerTestCase): ...@@ -149,7 +160,7 @@ class TestCourseStaff(XBlockHandlerTestCase):
@scenario('data/basic_scenario.xml', user_id='Bob') @scenario('data/basic_scenario.xml', user_id='Bob')
def test_staff_debug_student_info_no_submission(self, xblock): def test_staff_debug_student_info_no_submission(self, xblock):
# Simulate that we are course staff # Simulate that we are course staff
xblock.xmodule_runtime = self._create_mock_runtime( xblock.xmodule_runtime = self._create_mock_runtime(
xblock.scope_ids.usage_id, True, False, "Bob" xblock.scope_ids.usage_id, True, False, "Bob"
) )
request = namedtuple('Request', 'params') request = namedtuple('Request', 'params')
...@@ -161,14 +172,15 @@ class TestCourseStaff(XBlockHandlerTestCase): ...@@ -161,14 +172,15 @@ class TestCourseStaff(XBlockHandlerTestCase):
@scenario('data/peer_only_scenario.xml', user_id='Bob') @scenario('data/peer_only_scenario.xml', user_id='Bob')
def test_staff_debug_student_info_peer_only(self, xblock): def test_staff_debug_student_info_peer_only(self, xblock):
# Simulate that we are course staff # Simulate that we are course staff
xblock.xmodule_runtime = self._create_mock_runtime( xblock.xmodule_runtime = self._create_mock_runtime(
xblock.scope_ids.usage_id, True, False, "Bob" xblock.scope_ids.usage_id, True, False, "Bob"
) )
xblock.runtime._services['user'] = NullUserService()
bob_item = STUDENT_ITEM.copy() bob_item = STUDENT_ITEM.copy()
bob_item["item_id"] = xblock.scope_ids.usage_id bob_item["item_id"] = xblock.scope_ids.usage_id
# Create a submission for Bob, and corresponding workflow. # Create a submission for Bob, and corresponding workflow.
submission = sub_api.create_submission(bob_item, {'text':"Bob Answer"}) submission = sub_api.create_submission(bob_item, {'text': "Bob Answer"})
peer_api.on_start(submission["uuid"]) peer_api.on_start(submission["uuid"])
workflow_api.create_workflow(submission["uuid"], ['peer']) workflow_api.create_workflow(submission["uuid"], ['peer'])
...@@ -201,11 +213,11 @@ class TestCourseStaff(XBlockHandlerTestCase): ...@@ -201,11 +213,11 @@ class TestCourseStaff(XBlockHandlerTestCase):
xblock.xmodule_runtime = self._create_mock_runtime( xblock.xmodule_runtime = self._create_mock_runtime(
xblock.scope_ids.usage_id, True, False, "Bob" xblock.scope_ids.usage_id, True, False, "Bob"
) )
xblock.runtime._services['user'] = NullUserService()
bob_item = STUDENT_ITEM.copy() bob_item = STUDENT_ITEM.copy()
bob_item["item_id"] = xblock.scope_ids.usage_id bob_item["item_id"] = xblock.scope_ids.usage_id
# Create a submission for Bob, and corresponding workflow. # Create a submission for Bob, and corresponding workflow.
submission = sub_api.create_submission(bob_item, {'text':"Bob Answer"}) submission = sub_api.create_submission(bob_item, {'text': "Bob Answer"})
peer_api.on_start(submission["uuid"]) peer_api.on_start(submission["uuid"])
workflow_api.create_workflow(submission["uuid"], ['self']) workflow_api.create_workflow(submission["uuid"], ['self'])
...@@ -237,6 +249,7 @@ class TestCourseStaff(XBlockHandlerTestCase): ...@@ -237,6 +249,7 @@ class TestCourseStaff(XBlockHandlerTestCase):
xblock.xmodule_runtime = self._create_mock_runtime( xblock.xmodule_runtime = self._create_mock_runtime(
xblock.scope_ids.usage_id, True, False, "Bob" xblock.scope_ids.usage_id, True, False, "Bob"
) )
xblock.runtime._services['user'] = NullUserService()
bob_item = STUDENT_ITEM.copy() bob_item = STUDENT_ITEM.copy()
bob_item["item_id"] = xblock.scope_ids.usage_id bob_item["item_id"] = xblock.scope_ids.usage_id
...@@ -296,13 +309,14 @@ class TestCourseStaff(XBlockHandlerTestCase): ...@@ -296,13 +309,14 @@ class TestCourseStaff(XBlockHandlerTestCase):
xblock.xmodule_runtime = self._create_mock_runtime( xblock.xmodule_runtime = self._create_mock_runtime(
xblock.scope_ids.usage_id, True, False, "Bob" xblock.scope_ids.usage_id, True, False, "Bob"
) )
xblock.runtime._services['user'] = NullUserService()
bob_item = STUDENT_ITEM.copy() bob_item = STUDENT_ITEM.copy()
bob_item["item_id"] = xblock.scope_ids.usage_id bob_item["item_id"] = xblock.scope_ids.usage_id
# Create an image submission for Bob # Create an image submission for Bob
sub_api.create_submission(bob_item, { sub_api.create_submission(bob_item, {
'text':"Bob Answer", 'text': "Bob Answer",
'file_key': "test_key" 'file_key': "test_key"
}) })
...@@ -318,23 +332,24 @@ class TestCourseStaff(XBlockHandlerTestCase): ...@@ -318,23 +332,24 @@ class TestCourseStaff(XBlockHandlerTestCase):
self.assertEquals('http://www.example.com/image.jpeg', context['submission']['image_url']) self.assertEquals('http://www.example.com/image.jpeg', context['submission']['image_url'])
# Check the fully rendered template # Check the fully rendered template
payload = urllib.urlencode({"student_id": "Bob"}) payload = urllib.urlencode({"student_username": "Bob"})
resp = self.request(xblock, "render_student_info", payload) resp = self.request(xblock, "render_student_info", payload)
self.assertIn("http://www.example.com/image.jpeg", resp) self.assertIn("http://www.example.com/image.jpeg", resp)
@scenario('data/self_only_scenario.xml', user_id='Bob') @scenario('data/self_only_scenario.xml', user_id='Bob')
def test_staff_debug_student_info_file_download_url_error(self, xblock): def test_staff_debug_student_info_file_download_url_error(self, xblock):
# Simulate that we are course staff # Simulate that we are course staff
xblock.xmodule_runtime = self._create_mock_runtime( xblock.xmodule_runtime = self._create_mock_runtime(
xblock.scope_ids.usage_id, True, False, "Bob" xblock.scope_ids.usage_id, True, False, "Bob"
) )
xblock.runtime._services['user'] = NullUserService()
bob_item = STUDENT_ITEM.copy() bob_item = STUDENT_ITEM.copy()
bob_item["item_id"] = xblock.scope_ids.usage_id bob_item["item_id"] = xblock.scope_ids.usage_id
# Create an image submission for Bob # Create an image submission for Bob
sub_api.create_submission(bob_item, { sub_api.create_submission(bob_item, {
'text':"Bob Answer", 'text': "Bob Answer",
'file_key': "test_key" 'file_key': "test_key"
}) })
...@@ -348,7 +363,7 @@ class TestCourseStaff(XBlockHandlerTestCase): ...@@ -348,7 +363,7 @@ class TestCourseStaff(XBlockHandlerTestCase):
self.assertNotIn('image_url', context['submission']) self.assertNotIn('image_url', context['submission'])
# Check the fully rendered template # Check the fully rendered template
payload = urllib.urlencode({"student_id": "Bob"}) payload = urllib.urlencode({"student_username": "Bob"})
resp = self.request(xblock, "render_student_info", payload) resp = self.request(xblock, "render_student_info", payload)
self.assertIn("Bob Answer", resp) self.assertIn("Bob Answer", resp)
...@@ -359,6 +374,7 @@ class TestCourseStaff(XBlockHandlerTestCase): ...@@ -359,6 +374,7 @@ class TestCourseStaff(XBlockHandlerTestCase):
xblock.xmodule_runtime = self._create_mock_runtime( xblock.xmodule_runtime = self._create_mock_runtime(
xblock.scope_ids.usage_id, True, False, "Bob" xblock.scope_ids.usage_id, True, False, "Bob"
) )
xblock.runtime._services['user'] = NullUserService()
# Commonly chosen options for assessments # Commonly chosen options for assessments
options_selected = { options_selected = {
...@@ -377,7 +393,7 @@ class TestCourseStaff(XBlockHandlerTestCase): ...@@ -377,7 +393,7 @@ class TestCourseStaff(XBlockHandlerTestCase):
bob_item["item_id"] = xblock.scope_ids.usage_id bob_item["item_id"] = xblock.scope_ids.usage_id
# Create a submission for Bob, and corresponding workflow. # Create a submission for Bob, and corresponding workflow.
submission = sub_api.create_submission(bob_item, {'text':"Bob Answer"}) submission = sub_api.create_submission(bob_item, {'text': "Bob Answer"})
peer_api.on_start(submission["uuid"]) peer_api.on_start(submission["uuid"])
workflow_api.create_workflow(submission["uuid"], ['peer', 'self']) workflow_api.create_workflow(submission["uuid"], ['peer', 'self'])
...@@ -410,7 +426,7 @@ class TestCourseStaff(XBlockHandlerTestCase): ...@@ -410,7 +426,7 @@ class TestCourseStaff(XBlockHandlerTestCase):
# Now Bob should be fully populated in the student info view. # Now Bob should be fully populated in the student info view.
request = namedtuple('Request', 'params') request = namedtuple('Request', 'params')
request.params = {"student_id": "Bob"} request.params = {"student_username": "Bob"}
# Verify that we can render without error # Verify that we can render without error
resp = xblock.render_student_info(request) resp = xblock.render_student_info(request)
self.assertIn("bob answer", resp.body.lower()) self.assertIn("bob answer", resp.body.lower())
......
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