Commit f79b231d by E. Kolpakov

student_view_user_state -> XBlock.handler

parent ef6d54ad
...@@ -115,12 +115,12 @@ class AnswerMixin(XBlockWithPreviewMixin, XBlockWithTranslationServiceMixin, Stu ...@@ -115,12 +115,12 @@ class AnswerMixin(XBlockWithPreviewMixin, XBlockWithTranslationServiceMixin, Stu
if not data.name: if not data.name:
add_error(u"A Question ID is required.") add_error(u"A Question ID is required.")
def student_view_user_state(self, context=None): def build_user_state_data(self, context=None):
""" """
Returns a JSON representation of the student data of this XBlock, Returns a JSON representation of the student data of this XBlock,
retrievable from the Course Block API. retrievable from the Course Block API.
""" """
result = super(AnswerMixin, self).student_view_user_state(context) result = super(AnswerMixin, self).build_user_state_data(context)
answer_data = self.get_model_object() answer_data = self.get_model_object()
result["answer_data"] = { result["answer_data"] = {
"student_input": answer_data.student_input, "student_input": answer_data.student_input,
......
import json
import webob
from lazy import lazy from lazy import lazy
from problem_builder.tests.unit.utils import DateTimeEncoder
from xblock.core import XBlock
from xblock.fields import String, Boolean, Float, Scope, UNIQUE_ID from xblock.fields import String, Boolean, Float, Scope, UNIQUE_ID
from xblock.fragment import Fragment from xblock.fragment import Fragment
from xblockutils.helpers import child_isinstance from xblockutils.helpers import child_isinstance
...@@ -185,7 +190,7 @@ class StudentViewUserStateMixin(object): ...@@ -185,7 +190,7 @@ class StudentViewUserStateMixin(object):
NESTED_BLOCKS_KEY = "components" NESTED_BLOCKS_KEY = "components"
INCLUDE_SCOPES = (Scope.user_state, Scope.user_info, Scope.preferences) INCLUDE_SCOPES = (Scope.user_state, Scope.user_info, Scope.preferences)
def student_view_user_state(self, context=None): def build_user_state_data(self, context=None):
""" """
Returns a JSON representation of the student data of this XBlock, Returns a JSON representation of the student data of this XBlock,
retrievable from the Course Block API. retrievable from the Course Block API.
...@@ -196,12 +201,23 @@ class StudentViewUserStateMixin(object): ...@@ -196,12 +201,23 @@ class StudentViewUserStateMixin(object):
result[field.name] = field.read_from(self) result[field.name] = field.read_from(self)
if getattr(self, "has_children", False): if getattr(self, "has_children", False):
components = [] components = {}
for child_id in self.children: for child_id in self.children:
child = self.runtime.get_block(child_id) child = self.runtime.get_block(child_id)
if hasattr(child, 'student_view_user_state'): if hasattr(child, 'build_user_state_data'):
components.append(child.student_view_user_state(context)) components[str(child_id)] = child.build_user_state_data(context)
result[self.NESTED_BLOCKS_KEY] = components result[self.NESTED_BLOCKS_KEY] = components
return result return result
@XBlock.handler
def student_view_user_state(self, context=None, suffix=''):
"""
Returns a JSON representation of the student data of this XBlock,
retrievable from the Course Block API.
"""
result = self.build_user_state_data(context)
json_result = json.dumps(result, cls=DateTimeEncoder)
return webob.response.Response(body=json_result, content_type='application/json')
""" """
Unit tests for AnswerMixin. Unit tests for AnswerMixin.
""" """
import json
import unittest import unittest
from collections import namedtuple from collections import namedtuple
from datetime import datetime from datetime import datetime
...@@ -62,7 +63,7 @@ class TestAnswerMixin(unittest.TestCase): ...@@ -62,7 +63,7 @@ class TestAnswerMixin(unittest.TestCase):
model = answer_mixin.get_model_object() model = answer_mixin.get_model_object()
self.assertEqual(model.course_key, course_id) self.assertEqual(model.course_key, course_id)
def test_student_view_user_state(self): def test_build_user_state_data(self):
name = 'test-course-key-2' name = 'test-course-key-2'
existing_model = Answer( existing_model = Answer(
name=name, name=name,
...@@ -74,7 +75,7 @@ class TestAnswerMixin(unittest.TestCase): ...@@ -74,7 +75,7 @@ class TestAnswerMixin(unittest.TestCase):
) )
existing_model.save() existing_model.save()
answer_mixin = self.make_answer_mixin(name=name) answer_mixin = self.make_answer_mixin(name=name)
student_view_user_state = answer_mixin.student_view_user_state() student_view_user_state = answer_mixin.build_user_state_data()
expected_user_state_data = { expected_user_state_data = {
"answer_data": { "answer_data": {
...@@ -84,3 +85,28 @@ class TestAnswerMixin(unittest.TestCase): ...@@ -84,3 +85,28 @@ class TestAnswerMixin(unittest.TestCase):
} }
} }
self.assertEqual(student_view_user_state, expected_user_state_data) self.assertEqual(student_view_user_state, expected_user_state_data)
def test_student_view_user_state(self):
name = 'test-course-key-3'
existing_model = Answer(
name=name,
student_id=self.anonymous_student_id,
course_key=self.course_id,
student_input="Test",
created_on=datetime(2017, 1, 2, 3, 4, 5),
modified_on=datetime(2017, 7, 8, 9, 10, 11),
)
existing_model.save()
answer_mixin = self.make_answer_mixin(name=name)
student_view_user_state = answer_mixin.student_view_user_state()
parsed_student_state = json.loads(student_view_user_state.body)
expected_user_state_data = {
"answer_data": {
"student_input": existing_model.student_input,
"created_on": existing_model.created_on.isoformat(),
"modified_on": existing_model.modified_on.isoformat(),
}
}
self.assertEqual(parsed_student_state, expected_user_state_data)
import json
import unittest import unittest
from datetime import datetime
import pytz
from mock import MagicMock, Mock from mock import MagicMock, Mock
from xblock.core import XBlock from xblock.core import XBlock
from xblock.field_data import DictFieldData from xblock.field_data import DictFieldData
from xblock.fields import String, Scope, Boolean, Integer from xblock.fields import String, Scope, Boolean, Integer, DateTime
from problem_builder.mixins import StudentViewUserStateMixin from problem_builder.mixins import StudentViewUserStateMixin
...@@ -22,7 +25,7 @@ class UserStateFieldsMixin(object): ...@@ -22,7 +25,7 @@ class UserStateFieldsMixin(object):
preference_2 = Integer(name="pref2", scope=Scope.preferences) preference_2 = Integer(name="pref2", scope=Scope.preferences)
user_info_1 = String(name="info1", scope=Scope.user_info) user_info_1 = String(name="info1", scope=Scope.user_info)
user_info_2 = Integer(name="info2", scope=Scope.user_info) user_info_2 = DateTime(name="info2", scope=Scope.user_info)
class ChildrenMixin(object): class ChildrenMixin(object):
...@@ -76,7 +79,7 @@ class TestStudentViewUserStateMixin(unittest.TestCase): ...@@ -76,7 +79,7 @@ class TestStudentViewUserStateMixin(unittest.TestCase):
def test_no_user_state_returns_empty(self): def test_no_user_state_returns_empty(self):
block = self._build_block(XBlockWithNoUserState, {"scope_settings": "qwe", "scope_content": "ASD"}) block = self._build_block(XBlockWithNoUserState, {"scope_settings": "qwe", "scope_content": "ASD"})
self.assertEqual(block.student_view_user_state(), {}) self.assertEqual(block.build_user_state_data(), {})
def test_no_child_blocks_with_user_state(self): def test_no_child_blocks_with_user_state(self):
user_fields = { user_fields = {
...@@ -85,19 +88,19 @@ class TestStudentViewUserStateMixin(unittest.TestCase): ...@@ -85,19 +88,19 @@ class TestStudentViewUserStateMixin(unittest.TestCase):
"preference_1": "Yes", "preference_1": "Yes",
"preference_2": 12, "preference_2": 12,
"user_info_1": "John", "user_info_1": "John",
"user_info_2": 27 "user_info_2": datetime(2017, 1, 2, 3, 4, 5, tzinfo=pytz.UTC)
} }
other_fields = {"setting": "setting", "content": "content", "user_state_summary": "Something"} other_fields = {"setting": "setting", "content": "content", "user_state_summary": "Something"}
block_fields = self._merge_dicts(user_fields, other_fields) block_fields = self._merge_dicts(user_fields, other_fields)
block = self._build_block(XBlockNoChildrenWithUserState, block_fields) block = self._build_block(XBlockNoChildrenWithUserState, block_fields)
self.assertEqual(block.student_view_user_state(), user_fields) self.assertEqual(block.build_user_state_data(), user_fields)
def test_children_empty_no_user_state(self): def test_children_empty_no_user_state(self):
block = self._build_block(XBlockChildrenNoUserState, {"scope_settings": "qwe", "scope_content": "ASD"}) block = self._build_block(XBlockChildrenNoUserState, {"scope_settings": "qwe", "scope_content": "ASD"})
self.assertEqual(block.children, []) # precondition self.assertEqual(block.children, []) # precondition
self.assertEqual(block.student_view_user_state(), {"components": []}) self.assertEqual(block.build_user_state_data(), {"components": []})
def test_children_no_user_state(self): def test_children_no_user_state(self):
block = self._build_block(XBlockChildrenNoUserState, {"scope_settings": "qwe", "scope_content": "ASD"}) block = self._build_block(XBlockChildrenNoUserState, {"scope_settings": "qwe", "scope_content": "ASD"})
...@@ -111,7 +114,7 @@ class TestStudentViewUserStateMixin(unittest.TestCase): ...@@ -111,7 +114,7 @@ class TestStudentViewUserStateMixin(unittest.TestCase):
self.assertEqual(self._runtime.get_block("child1"), no_user_state1) self.assertEqual(self._runtime.get_block("child1"), no_user_state1)
self.assertEqual(self._runtime.get_block("child2"), no_user_state2) self.assertEqual(self._runtime.get_block("child2"), no_user_state2)
student_user_state = block.student_view_user_state() student_user_state = block.build_user_state_data()
expected = {"components": [{}, {}]} expected = {"components": [{}, {}]}
self.assertEqual(student_user_state, expected) self.assertEqual(student_user_state, expected)
...@@ -126,7 +129,7 @@ class TestStudentViewUserStateMixin(unittest.TestCase): ...@@ -126,7 +129,7 @@ class TestStudentViewUserStateMixin(unittest.TestCase):
"preference_1": "Yes", "preference_1": "Yes",
"preference_2": 12, "preference_2": 12,
"user_info_1": "John", "user_info_1": "John",
"user_info_2": 27 "user_info_2": datetime(2017, 1, 2, 3, 4, 5, tzinfo=pytz.UTC)
} }
user_fields2 = { user_fields2 = {
"answer_1": "BBBB", "answer_1": "BBBB",
...@@ -134,7 +137,7 @@ class TestStudentViewUserStateMixin(unittest.TestCase): ...@@ -134,7 +137,7 @@ class TestStudentViewUserStateMixin(unittest.TestCase):
"preference_1": "No", "preference_1": "No",
"preference_2": 7, "preference_2": 7,
"user_info_1": "jane", "user_info_1": "jane",
"user_info_2": 19 "user_info_2": datetime(2017, 1, 2, 3, 4, 5, tzinfo=pytz.UTC)
} }
user_state1 = self._build_block(XBlockNoChildrenWithUserState, self._merge_dicts(user_fields1, other_fields)) user_state1 = self._build_block(XBlockNoChildrenWithUserState, self._merge_dicts(user_fields1, other_fields))
user_state2 = self._build_block(XBlockNoChildrenWithUserState, self._merge_dicts(user_fields2, other_fields)) user_state2 = self._build_block(XBlockNoChildrenWithUserState, self._merge_dicts(user_fields2, other_fields))
...@@ -146,7 +149,7 @@ class TestStudentViewUserStateMixin(unittest.TestCase): ...@@ -146,7 +149,7 @@ class TestStudentViewUserStateMixin(unittest.TestCase):
self.assertEqual(self._runtime.get_block("child1"), user_state1) self.assertEqual(self._runtime.get_block("child1"), user_state1)
self.assertEqual(self._runtime.get_block("child2"), user_state2) self.assertEqual(self._runtime.get_block("child2"), user_state2)
student_user_state = block.student_view_user_state() student_user_state = block.build_user_state_data()
expected = {"components": [user_fields1, user_fields2]} expected = {"components": [user_fields1, user_fields2]}
self.assertEqual(student_user_state, expected) self.assertEqual(student_user_state, expected)
...@@ -159,7 +162,7 @@ class TestStudentViewUserStateMixin(unittest.TestCase): ...@@ -159,7 +162,7 @@ class TestStudentViewUserStateMixin(unittest.TestCase):
"preference_1": "IDN", "preference_1": "IDN",
"preference_2": 42, "preference_2": 42,
"user_info_1": "Douglas", "user_info_1": "Douglas",
"user_info_2": 9 "user_info_2": datetime(2017, 1, 2, 3, 4, 5, tzinfo=pytz.UTC)
} }
block = self._build_block(XBlockChildrenUserState, self._merge_dicts(user_fields, other_fields)) block = self._build_block(XBlockChildrenUserState, self._merge_dicts(user_fields, other_fields))
...@@ -169,7 +172,7 @@ class TestStudentViewUserStateMixin(unittest.TestCase): ...@@ -169,7 +172,7 @@ class TestStudentViewUserStateMixin(unittest.TestCase):
"preference_1": "Yes", "preference_1": "Yes",
"preference_2": 12, "preference_2": 12,
"user_info_1": "John", "user_info_1": "John",
"user_info_2": 27 "user_info_2": datetime(2017, 1, 2, 3, 4, 5, tzinfo=pytz.UTC)
} }
user_state = self._build_block( user_state = self._build_block(
XBlockNoChildrenWithUserState, self._merge_dicts(nested_user_fields, other_fields) XBlockNoChildrenWithUserState, self._merge_dicts(nested_user_fields, other_fields)
...@@ -181,8 +184,48 @@ class TestStudentViewUserStateMixin(unittest.TestCase): ...@@ -181,8 +184,48 @@ class TestStudentViewUserStateMixin(unittest.TestCase):
self.assertEqual(block.children, nested.keys()) self.assertEqual(block.children, nested.keys())
self.assertEqual(self._runtime.get_block("child1"), user_state) self.assertEqual(self._runtime.get_block("child1"), user_state)
student_user_state = block.student_view_user_state() student_user_state = block.build_user_state_data()
expected = user_fields.copy() expected = user_fields.copy()
expected["components"] = [nested_user_fields] expected["components"] = [nested_user_fields]
self.assertEqual(student_user_state, expected) self.assertEqual(student_user_state, expected)
def test_user_state_handler(self):
other_fields = {"setting": "setting", "content": "content", "user_state_summary": "Something"}
user_fields = {
"answer_1": "OOOO",
"answer_2": True,
"preference_1": "IDN",
"preference_2": 42,
"user_info_1": "Douglas",
"user_info_2": datetime(2017, 1, 2, 3, 4, 5, tzinfo=pytz.UTC)
}
block = self._build_block(XBlockChildrenUserState, self._merge_dicts(user_fields, other_fields))
nested_user_fields = {
"answer_1": "AAAA",
"answer_2": False,
"preference_1": "Yes",
"preference_2": 12,
"user_info_1": "John",
"user_info_2": datetime(2017, 1, 2, 3, 4, 5, tzinfo=pytz.UTC)
}
user_state = self._build_block(
XBlockNoChildrenWithUserState, self._merge_dicts(nested_user_fields, other_fields)
)
nested = {"child1": user_state}
self._set_children(block, nested)
# preconditions
self.assertEqual(block.children, nested.keys())
self.assertEqual(self._runtime.get_block("child1"), user_state)
student_user_state_response = block.student_view_user_state()
student_user_state = json.loads(student_user_state_response.body)
expected = user_fields.copy()
expected["user_info_2"] = expected["user_info_2"].isoformat()
nested_copy = nested_user_fields.copy()
nested_copy["user_info_2"] = nested_copy["user_info_2"].isoformat()
expected["components"] = [nested_copy]
self.assertEqual(student_user_state, expected)
""" """
Helper methods for testing Problem Builder / Step Builder blocks Helper methods for testing Problem Builder / Step Builder blocks
""" """
import json
from datetime import datetime, date
from mock import MagicMock, Mock, patch from mock import MagicMock, Mock, patch
from xblock.field_data import DictFieldData from xblock.field_data import DictFieldData
...@@ -87,3 +90,11 @@ def instantiate_block(cls, fields=None): ...@@ -87,3 +90,11 @@ def instantiate_block(cls, fields=None):
block.children = children block.children = children
block.runtime.get_block = lambda child_id: children[child_id] block.runtime.get_block = lambda child_id: children[child_id]
return block return block
class DateTimeEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, (datetime, date)):
return o.isoformat()
return json.JSONEncoder.default(self, o)
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