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
......@@ -186,8 +186,8 @@ for 'Private Results':
![Private Results](doc_img/private_results.png)
**Notes on Private Results**: Users will be able to change their vote on polls and surveys with this option enabled.
An analytics event will not be fired upon the student viewing the results, as the results are never visible. A user
will see a thank you message, and optionally, any instructor-provided Feedback in an additional "Feedback" section,
An analytics event will not be fired upon the student viewing the results, as the results are never visible. A user
will see a thank you message, and optionally, any instructor-provided Feedback in an additional "Feedback" section,
when they click submit:
![Private Results Submission](doc_img/private_results_submission.png)
......@@ -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
## 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 -*-
#
# Copyright (C) 2015 McKinsey Academy
......@@ -20,12 +21,10 @@
# along with this program in a file in the toplevel directory called
# "AGPLv3". If not, see <http://www.gnu.org/licenses/>.
#
from collections import OrderedDict
import functools
import json
from django.template import Template, Context
from markdown import markdown
import pkg_resources
from webob import Response
......@@ -41,6 +40,7 @@ from .utils import _
try:
# pylint: disable=import-error
from django.conf import settings
from django.template import Template, Context
from api_manager.models import GroupProfile
HAS_GROUP_PROFILE = True
except ImportError:
......@@ -133,8 +133,8 @@ class PollBase(XBlock, ResourceMixin, PublishEventMixin):
"""
if hasattr(self, 'location'):
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):
"""
......@@ -393,8 +393,8 @@ class PollBlock(PollBase):
"""
if self.choice and self.choice in dict(self.answers):
return self.choice
else:
return None
return None
def student_view(self, context=None):
"""
......@@ -434,6 +434,33 @@ class PollBlock(PollBase):
context, "public/html/poll.html", "public/css/poll.css",
"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):
if not context:
context = {}
......@@ -664,6 +691,33 @@ class SurveyBlock(PollBase):
context, "public/html/survey.html", "public/css/poll.css",
"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):
"""
Render markdown for questions, and annotate with answers
......
......@@ -44,7 +44,7 @@ def package_data(pkg, roots):
setup(
name='xblock-poll',
version='1.2.7',
version='1.3.0',
description='An XBlock for polling users.',
packages=[
'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