Commit ed40affe by Braden MacDonald Committed by GitHub

Merge pull request #31 from msaqib52/master

Add student_view_data to poll and survey xblock
parents 7ba819b9 9db6b11d
...@@ -250,3 +250,153 @@ tx pull -f --mode=reviewed -l en,ar,es_419,fr,he,hi,ko_KR,pt_BR,ru,zh_CN ...@@ -250,3 +250,153 @@ tx pull -f --mode=reviewed -l en,ar,es_419,fr,he,hi,ko_KR,pt_BR,ru,zh_CN
``` ```
[transifex-client]: https://docs.transifex.com/client/installing-the-client [transifex-client]: https://docs.transifex.com/client/installing-the-client
## API for native mobile frontends
**Retrieve fixed data for all poll/survey XBlocks in a course:**
```
GET https://<lms_server_url>/api/courses/v1/blocks/?course_id=<course_id>&username=<username>&depth=all&requested_fields=student_view_data
```
Example poll return value:
```
"student_view_data": {
"question": "Did the explanation above make sense to you?",
"answers": [
[
"R",
{
"img_alt": "",
"img": "",
"label": "Yes, completely"
}
],
[
"B",
{
"img_alt": "",
"img": "",
"label": "Yes, for the most part"
}
],
[
"G",
{
"img_alt": "",
"img": "",
"label": "Not really"
}
],
[
"O",
{
"img_alt": "",
"img": "",
"label": "Not at all"
}
]
]
},
```
Example survey return value:
```
"student_view_data": {
"answers": [
[
"Y",
"Yes"
],
[
"N",
"No"
],
[
"M",
"Maybe"
],
[
"1464806559402",
"Unsure"
]
],
"questions": [
[
"enjoy",
{
"img_alt": "",
"img": "",
"label": "Do you think you will use Polls in your course?"
}
],
[
"recommend",
{
"img_alt": "",
"img": "",
"label": "Do you think you will use Surveys in your course?"
}
],
[
"learn",
{
"img_alt": "",
"img": "",
"label": "Do you think the ability to query students is useful?"
}
],
[
"1464806513240",
{
"img_alt": "",
"img": "",
"label": "Do you like taking Polls and/or Surveys?"
}
]
]
},
```
**Retrieve current poll tally and current user's vote**
```
GET https://<lms_server_url>/courses/<course_id>/xblock/<poll_xblock_id>/handler/student_view_user_state
```
Example return value:
```
{"tally": {"B": 0, "R": 1, "O": 0, "G": 0}, "submissions_count": 1, "choice": "R"}
```
**Retrieve current survey tally and current user's vote**
```
GET https://<lms_server_url>/courses/<course_id>/xblock/<survey_xblock_id>/handler/student_view_user_state
```
Example return value:
```
{
"tally":{
"enjoy":{
"Y":1,
"M":0,
"N":0
},
"learn":{
"Y":0,
"M":1,
"N":0
},
"recommend":{
"Y":0,
"M":0,
"N":1
}
},
"submissions_count":1,
"choices":{
"enjoy":"Y",
"recommend":"N",
"learn":"M"
}
}
```
# pylint: disable=too-many-lines
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# Copyright (C) 2015 McKinsey Academy # Copyright (C) 2015 McKinsey Academy
...@@ -20,12 +21,10 @@ ...@@ -20,12 +21,10 @@
# along with this program in a file in the toplevel directory called # along with this program in a file in the toplevel directory called
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>. # "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
# #
from collections import OrderedDict from collections import OrderedDict
import functools import functools
import json import json
from django.template import Template, Context
from markdown import markdown from markdown import markdown
import pkg_resources import pkg_resources
from webob import Response from webob import Response
...@@ -41,6 +40,7 @@ from .utils import _ ...@@ -41,6 +40,7 @@ from .utils import _
try: try:
# pylint: disable=import-error # pylint: disable=import-error
from django.conf import settings from django.conf import settings
from django.template import Template, Context
from api_manager.models import GroupProfile from api_manager.models import GroupProfile
HAS_GROUP_PROFILE = True HAS_GROUP_PROFILE = True
except ImportError: except ImportError:
...@@ -133,7 +133,7 @@ class PollBase(XBlock, ResourceMixin, PublishEventMixin): ...@@ -133,7 +133,7 @@ class PollBase(XBlock, ResourceMixin, PublishEventMixin):
""" """
if hasattr(self, 'location'): if hasattr(self, 'location'):
return self.location.html_id() # pylint: disable=no-member return self.location.html_id() # pylint: disable=no-member
else:
return unicode(self.scope_ids.usage_id) return unicode(self.scope_ids.usage_id)
def img_alt_mandatory(self): def img_alt_mandatory(self):
...@@ -393,7 +393,7 @@ class PollBlock(PollBase): ...@@ -393,7 +393,7 @@ class PollBlock(PollBase):
""" """
if self.choice and self.choice in dict(self.answers): if self.choice and self.choice in dict(self.answers):
return self.choice return self.choice
else:
return None return None
def student_view(self, context=None): def student_view(self, context=None):
...@@ -434,6 +434,33 @@ class PollBlock(PollBase): ...@@ -434,6 +434,33 @@ class PollBlock(PollBase):
context, "public/html/poll.html", "public/css/poll.css", context, "public/html/poll.html", "public/css/poll.css",
"public/js/poll.js", "PollBlock") "public/js/poll.js", "PollBlock")
def student_view_data(self, context=None):
"""
Returns a JSON representation of the poll Xblock, that can be retrieved
using Course Block API.
"""
return {
'question': self.question,
'answers': self.answers,
}
@XBlock.handler
def student_view_user_state(self, data, suffix=''):
"""
Returns a JSON representation of the student data for Poll Xblock
"""
response = {
'choice': self.get_choice(),
'tally': self.tally,
'submissions_count': self.submissions_count,
}
return Response(
json.dumps(response),
content_type='application/json',
charset='utf8'
)
def studio_view(self, context=None): def studio_view(self, context=None):
if not context: if not context:
context = {} context = {}
...@@ -664,6 +691,33 @@ class SurveyBlock(PollBase): ...@@ -664,6 +691,33 @@ class SurveyBlock(PollBase):
context, "public/html/survey.html", "public/css/poll.css", context, "public/html/survey.html", "public/css/poll.css",
"public/js/poll.js", "SurveyBlock") "public/js/poll.js", "SurveyBlock")
def student_view_data(self, context=None):
"""
Returns a JSON representation of survey XBlock, that can be retrieved
using Course Block API.
"""
return {
'questions': self.questions,
'answers': self.answers,
}
@XBlock.handler
def student_view_user_state(self, data, suffix=''):
"""
Returns a JSON representation of the student data for Survey Xblock
"""
response = {
'choices': self.get_choices(),
'tally': self.tally,
'submissions_count': self.submissions_count,
}
return Response(
json.dumps(response),
content_type='application/json',
charset='utf8'
)
def renderable_answers(self, questions, choices): def renderable_answers(self, questions, choices):
""" """
Render markdown for questions, and annotate with answers Render markdown for questions, and annotate with answers
......
...@@ -44,7 +44,7 @@ def package_data(pkg, roots): ...@@ -44,7 +44,7 @@ def package_data(pkg, roots):
setup( setup(
name='xblock-poll', name='xblock-poll',
version='1.2.7', version='1.3.0',
description='An XBlock for polling users.', description='An XBlock for polling users.',
packages=[ packages=[
'poll', 'poll',
......
import unittest
import json
from xblock.field_data import DictFieldData
from poll.poll import PollBlock, SurveyBlock
from ..utils import MockRuntime, make_request
class TestPollBlock(unittest.TestCase):
"""
Tests for XBlock Poll.
"""
def setUp(self):
"""
Test case setup
"""
super(TestPollBlock, self).setUp()
self.runtime = MockRuntime()
self.poll_data = {
'display_name': 'My Poll',
'question': 'What is your favorite color?',
'answers': [
['R', {'label': 'Red'}],
['B', {'label': 'Blue'}],
['G', {'label': 'Green'}],
['O', {'label': 'Other'}],
],
'submissions_count': 5,
}
self.poll_block = PollBlock(
self.runtime,
DictFieldData(self.poll_data),
None
)
def test_student_view_data(self):
"""
Test the student_view_data results.
"""
expected_poll_data = {
'question': self.poll_data['question'],
'answers': self.poll_data['answers'],
}
student_view_data = self.poll_block.student_view_data()
self.assertEqual(student_view_data, expected_poll_data)
def test_student_view_user_state_handler(self):
"""
Test the student_view_user_state handler results.
"""
response = json.loads(
self.poll_block.handle(
'student_view_user_state',
make_request('', method='GET')
).body
)
expected_response = {
u'choice': None,
u'submissions_count': 5,
u'tally': {'R': 0, 'B': 0, 'G': 0, 'O': 0},
}
self.assertEqual(response, expected_response)
class TestSurveyBlock(unittest.TestCase):
"""
Tests for XBlock Survey.
"""
def setUp(self):
"""
Test case setup
"""
super(TestSurveyBlock, self).setUp()
self.runtime = MockRuntime()
self.survery_data = {
'display_name': 'My Survey',
'questions': [
['enjoy', {'label': 'Are you enjoying the course?'}],
['recommend', {'label': 'Would you recommend this course to your friends?'}],
['learn', {'label': 'Do you think you will learn a lot?'}]
],
'answers': [
['Y', 'Yes'],
['N', 'No'],
['M', 'Maybe']
],
'submissions_count': 5
}
self.survey_block = SurveyBlock(
self.runtime,
DictFieldData(self.survery_data),
None
)
def test_student_view_data(self):
"""
Test the student_view_data results.
"""
expected_survery_data = {
'questions': self.survery_data['questions'],
'answers': self.survery_data['answers'],
}
student_view_data = self.survey_block.student_view_data()
self.assertEqual(student_view_data, expected_survery_data)
def test_student_view_user_state_handler(self):
"""
Test the student_view_user_state handler results.
"""
response = json.loads(
self.survey_block.handle(
'student_view_user_state',
make_request('', method='GET')
).body
)
expected_response = {
u'choices': None,
u'submissions_count': 5,
u'tally': {
u'enjoy': {u'M': 0, u'N': 0, u'Y': 0},
u'learn': {u'M': 0, u'N': 0, u'Y': 0},
u'recommend': {u'M': 0, u'N': 0, u'Y': 0},
},
}
self.assertEqual(response, expected_response)
# Test mocks and helpers
from webob import Request
from xblock.runtime import DictKeyValueStore, KvsFieldData
from xblock.test.tools import TestRuntime
def make_request(body, method='POST'):
"""
Helper method to make request
"""
request = Request.blank('/')
request.body = body.encode('utf-8')
request.method = method
return request
# pylint: disable=abstract-method
class MockRuntime(TestRuntime):
"""
Provides a mock XBlock runtime object.
"""
def __init__(self, **kwargs):
field_data = kwargs.get('field_data', KvsFieldData(DictKeyValueStore()))
super(MockRuntime, self).__init__(field_data=field_data)
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