Commit e5080f43 by Will Daly

Display an image response in the course staff debug page

parent 21aa930e
......@@ -13,6 +13,14 @@
<div class="student__answer__display__content">
{{ submission.answer.text|linebreaks }}
</div>
{% if submission.image_url %}
<img
class="submission--image"
alt="{% trans "The image associated with this response" %}"
src="{{ submission.image_url }}"
/>
{% endif %}
</div>
</div>
......
......@@ -4,6 +4,7 @@ determine the flow of the problem.
"""
import copy
from functools import wraps
import logging
from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy
......@@ -17,6 +18,10 @@ from submissions import api as submission_api
from openassessment.assessment.api import peer as peer_api
from openassessment.assessment.api import self as self_api
from openassessment.assessment.api import ai as ai_api
from openassessment.fileupload import api as file_api
logger = logging.getLogger(__name__)
def require_global_admin(error_msg):
......@@ -189,16 +194,19 @@ class StaffInfoMixin(object):
Must be course staff to render this view.
"""
path, context = self.get_student_info_path_and_context(data)
student_id = data.params.get('student_id', '')
path, context = self.get_student_info_path_and_context(student_id)
return self.render_assessment(path, context)
def get_student_info_path_and_context(self, data):
def get_student_info_path_and_context(self, student_id):
"""
Get the proper path and context for rendering the the student info
section of the staff debug panel.
Args:
student_id (unicode): The ID of the student to report.
"""
student_id = data.params.get('student_id', '')
submission_uuid = None
submission = None
assessment_steps = self.assessment_steps
......@@ -212,8 +220,22 @@ class StaffInfoMixin(object):
submissions = submission_api.get_submissions(student_item, 1)
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']
try:
submission['image_url'] = file_api.get_download_url(file_key)
except file_api.FileUploadError:
# Log the error, but do not prevent the rest of the student info
# from being displayed.
msg = (
u"Could not retrieve image URL for staff debug page. "
u"The student ID is '{student_id}', and the file key is {file_key}"
).format(student_id=student_id, file_key=file_key)
logger.exception(msg)
example_based_assessment = None
self_assessment = None
......
......@@ -100,7 +100,7 @@ class XBlockHandlerTestCase(CacheResetTest):
)
return self.runtime.get_block(block_id)
def request(self, xblock, handler_name, content, response_format=None):
def request(self, xblock, handler_name, content, request_method="POST", response_format=None):
"""
Make a request to an XBlock handler.
......@@ -110,6 +110,7 @@ class XBlockHandlerTestCase(CacheResetTest):
content (unicode): Content of the request.
Keyword Arguments:
request_method (str): The HTTP method of the request (defaults to POST)
response_format (None or str): Expected format of the response string.
If `None`, return the raw response content; if 'json', parse the
response as JSON and return the result.
......@@ -122,6 +123,7 @@ class XBlockHandlerTestCase(CacheResetTest):
"""
# Create a fake request
request = webob.Request(dict())
request.method = request_method
request.body = content
# Send the request to the XBlock handler
......
......@@ -2,6 +2,7 @@
from collections import namedtuple
import json
import datetime
import urllib
from mock import Mock, patch
from django.test.utils import override_settings
from openassessment.assessment.api import peer as peer_api
......@@ -9,6 +10,7 @@ from openassessment.assessment.api import self as self_api
from openassessment.assessment.api import ai as ai_api
from openassessment.workflow import api as workflow_api
from openassessment.assessment.errors.ai import AIError, AIGradingInternalError
from openassessment.fileupload.api import FileUploadInternalError
from submissions import api as sub_api
from openassessment.xblock.test.base import scenario, XBlockHandlerTestCase
......@@ -188,10 +190,7 @@ class TestCourseStaff(XBlockHandlerTestCase):
)
# Now Bob should be fully populated in the student info view.
request = namedtuple('Request', 'params')
request.params = {"student_id": "Bob"}
# Verify that we can render without error
path, context = xblock.get_student_info_path_and_context(request)
path, context = xblock.get_student_info_path_and_context("Bob")
self.assertEquals("Bob Answer", context['submission']['answer']['text'])
self.assertIsNone(context['self_assessment'])
self.assertEquals("openassessmentblock/staff_debug/student_info.html", path)
......@@ -220,15 +219,73 @@ class TestCourseStaff(XBlockHandlerTestCase):
{'criteria': xblock.rubric_criteria},
)
# Now Bob should be fully populated in the student info view.
request = namedtuple('Request', 'params')
request.params = {"student_id": "Bob"}
# Verify that we can render without error
path, context = xblock.get_student_info_path_and_context(request)
path, context = xblock.get_student_info_path_and_context("Bob")
self.assertEquals("Bob Answer", context['submission']['answer']['text'])
self.assertEquals([], context['peer_assessments'])
self.assertEquals("openassessmentblock/staff_debug/student_info.html", path)
@scenario('data/self_only_scenario.xml', user_id='Bob')
def test_staff_debug_student_info_image_submission(self, xblock):
# Simulate that we are course staff
xblock.xmodule_runtime = self._create_mock_runtime(
xblock.scope_ids.usage_id, True, False, "Bob"
)
bob_item = STUDENT_ITEM.copy()
bob_item["item_id"] = xblock.scope_ids.usage_id
# Create an image submission for Bob
sub_api.create_submission(bob_item, {
'text':"Bob Answer",
'file_key': "test_key"
})
# Mock the file upload API to avoid hitting S3
with patch("openassessment.xblock.staff_info_mixin.file_api") as file_api:
file_api.get_download_url.return_value = "http://www.example.com/image.jpeg"
__, context = xblock.get_student_info_path_and_context("Bob")
# Check that the right file key was passed to generate the download url
file_api.get_download_url.assert_called_with("test_key")
# Check the context passed to the template
self.assertEquals('http://www.example.com/image.jpeg', context['submission']['image_url'])
# Check the fully rendered template
payload = urllib.urlencode({"student_id": "Bob"})
resp = self.request(xblock, "render_student_info", payload)
self.assertIn("http://www.example.com/image.jpeg", resp)
@scenario('data/self_only_scenario.xml', user_id='Bob')
def test_staff_debug_student_info_file_download_url_error(self, xblock):
# Simulate that we are course staff
xblock.xmodule_runtime = self._create_mock_runtime(
xblock.scope_ids.usage_id, True, False, "Bob"
)
bob_item = STUDENT_ITEM.copy()
bob_item["item_id"] = xblock.scope_ids.usage_id
# Create an image submission for Bob
sub_api.create_submission(bob_item, {
'text':"Bob Answer",
'file_key': "test_key"
})
# Mock the file upload API to simulate an error
with patch("openassessment.xblock.staff_info_mixin.file_api.get_download_url") as file_api_call:
file_api_call.side_effect = FileUploadInternalError("Error!")
__, context = xblock.get_student_info_path_and_context("Bob")
# Expect that the page still renders, but without the image url
self.assertIn('submission', context)
self.assertNotIn('image_url', context['submission'])
# Check the fully rendered template
payload = urllib.urlencode({"student_id": "Bob"})
resp = self.request(xblock, "render_student_info", payload)
self.assertIn("Bob Answer", resp)
@override_settings(ORA2_AI_ALGORITHMS=AI_ALGORITHMS)
@scenario('data/example_based_assessment.xml', user_id='Bob')
def test_staff_debug_student_info_full_workflow(self, xblock):
......
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