Commit f56c62d4 by Jonathan Piacenti

CMS editing implemented but not working quite right yet.

parent 70d645fd
"""TO-DO: Write a description of what this XBlock is."""
from collections import OrderedDict
from django.template import Template, Context
import pkg_resources
......@@ -15,16 +16,18 @@ class PollBlock(XBlock):
"""
question = String(default='What is your favorite color?')
answers = List(
default=['Red', 'Blue', 'Green', 'Other'],
default=(('Red', 'Red'), ('Blue', 'Blue'), ('Green', 'Green'),
('Other', 'Other')),
scope=Scope.settings, help="The questions on this poll."
)
tally = Dict(default={1: 0, 2: 0, 3: 0, 4: 0}, scope=Scope.user_state_summary,
tally = Dict(default={'Red': 0, 'Blue': 0, 'Green': 0, 'Other': 0},
scope=Scope.user_state_summary,
help="Total tally of answers from students.")
# No default. Hopefully this will yield 'None', or do something
# distinctive when queried.
# Choices are always one above their place in the index so that choice
# is never false if it's provided.
choice = Integer(scope=Scope.user_state, help="The student's answer")
choice = String(scope=Scope.user_state, help="The student's answer")
def resource_string(self, path):
"""Handy helper for getting resources from our kit."""
......@@ -39,15 +42,17 @@ class PollBlock(XBlock):
"""
Tally all results.
"""
# TODO: Cache this.
tally = [{'count': 0, 'answer': answer, 'top': False}
for answer in self.answers
]
tally = []
answers = OrderedDict(self.answers)
total = 0
for key, value in self.tally.items():
key, value = int(key), int(value)
tally[key - 1]['count'] = value
total += value
for key, value in answers.items():
tally.append({
'count': int(self.tally.get(key, 0)),
'answer': value,
'key': key,
'top': False
})
total += tally[-1]['count']
for answer in tally:
try:
......@@ -71,15 +76,15 @@ class PollBlock(XBlock):
when viewing courses.
"""
if not context:
context = Context()
context = {}
js_template = self.resource_string(
'/static/handlebars/results.handlebars')
'/public/handlebars/results.handlebars')
context.update({
'choice': self.choice,
# Offset so choices will always be True.
'answers': zip(range(1, len(self.answers) + 1), self.answers),
'answers': self.answers,
'question': self.question,
'js_template': js_template,
})
......@@ -87,23 +92,91 @@ class PollBlock(XBlock):
if self.choice:
context.update({'tally': self.tally_detail()})
html = self.resource_string("static/html/poll.html")
context = Context(context)
html = self.resource_string("public/html/poll.html")
html = Template(html).render(context)
frag = Fragment(html)
frag.add_css(self.resource_string("static/css/poll.css"))
frag.add_javascript(self.resource_string("static/js/vendor/handlebars.js"))
frag.add_javascript(self.resource_string("static/js/src/poll.js"))
frag.add_css(self.resource_string("public/css/poll.css"))
frag.add_javascript_url(
self.runtime.local_resource_url(
self, 'public/js/vendor/handlebars.js'))
frag.add_javascript(self.resource_string("public/js/poll.js"))
frag.initialize_js('PollBlock')
return frag
# TO-DO: change this handler to perform your own actions. You may need more
# than one handler, or you may not need any handlers at all.
@XBlock.json_handler
def load_answers(self, data, suffix=''):
return {'answers': [{'key': key, 'text': answer}
for key, answer in self.answers
]}
def studio_view(self, context=None):
if not context:
context = {}
js_template = self.resource_string('/public/handlebars/studio.handlebars')
context.update({
'question': self.question,
'js_template': js_template
})
context = Context(context)
html = self.resource_string("public/html/poll_edit.html")
html = Template(html).render(context)
frag = Fragment(html)
frag.add_javascript_url(
self.runtime.local_resource_url(
self, 'public/js/vendor/handlebars.js'))
frag.add_css(self.resource_string('/public/css/poll_edit.css'))
frag.add_javascript(self.resource_string("public/js/poll_edit.js"))
frag.initialize_js('PollEditBlock')
return frag
@XBlock.json_handler
def studio_submit(self, data, suffix=''):
# I wonder if there's something for live validation feedback already.
result = {'success': True, 'errors': {'fields': {}, 'general': []}}
if 'question' not in data or not data['question']:
result['errors']['question'] = "This field is required."
result['success'] = False
# Aggressively clean/sanity check answers list.
answers = OrderedDict(
(key.replace('answer-', '', 1), value.strip()[:250])
for key, value in data.items()
if (key.startswith('answer-') and not key == 'answer-')
and not value.isspace()
)
if not len(answers) > 1:
result['errors']['general'].append(
"You must include at least two answers.")
result['success'] = False
if not result['success']:
return result
self.answers = answers.items()
self.question = data['question']
tally = self.tally
# Update tracking schema.
for key, value in answers.items():
if key not in tally:
tally[key] = 0
for key, value in tally.items():
if key not in answers:
del tally[key]
return result
@XBlock.json_handler
def vote(self, data, suffix=''):
"""
An example handler, which increments the data.
"""
result = {'result': 'error'}
result = {'success': False}
if self.choice is not None:
result['message'] = 'You cannot vote twice.'
return result
......@@ -114,15 +187,14 @@ class PollBlock(XBlock):
return result
# Just to show data coming in...
try:
choice = int(choice)
self.answers[choice]
except (IndexError, ValueError):
result['message'] = 'No index "{choice}" in answers list.'.format(choice=choice)
OrderedDict(self.answers)[choice]
except KeyError:
result['message'] = 'No key "{choice}" in answers table.'.format(choice=choice)
return result
self.choice = choice
self.tally[choice - 1] += 1
result['result'] = 'success'
self.tally[choice] += 1
result['success'] = True
return result
......
/* CSS for PollBlock */
.poll-delete-answer {
float: right;
}
\ No newline at end of file
<script id="answer-form-component" type="text/html">
{{#each answers}}
<li class="field comp-setting-entry is-set">
<div class="wrapper-comp-setting">
<label class="label setting-label" for="answer-{{key}}">Answer</label>
<input class="input setting-input" name="answer-{{key}}" id="answer-{{key}}" value="{{text}}" type="text" />
</div>
<span class="tip setting-help">Enter an answer for the user to select.</span>
<a href="#" class="button action-button poll-delete-answer" onclick="return false;">Delete</a>
</li>
{{/each}}
</script>
\ No newline at end of file
......@@ -5,14 +5,14 @@
<form>
<h2>{{ question }}</h2>
<ul>
{% for number, answer in answers %}
{% for key, answer in answers %}
<li class="poll-answer">
<label for="answer-{{number}}">{{answer}}</label>
<input type="radio" name="choice" id="answer-{{number}}" value="{{number}}">
<input type="radio" name="choice" id="answer-{{number}}" value="{{key}}">
</li>
{% endfor %}
</ul>
<input type="submit" name="poll-submit" onclick="return false;">
<input type="submit" name="poll-submit" onclick="return false;" disabled>
</form>
{% endif %}
</div>
\ No newline at end of file
{{js_template|safe}}
<div class="wrapper-comp-settings is-active editor-with-buttons" id="settings-tab">
<form id="poll-form">
<ul class="list-input settings-list" id="poll-line-items">
<li class="field comp-setting-entry is-set">
<div class="wrapper-comp-setting">
<label class="label setting-label" for="question">Question</label>
<input class="input setting-input" name="question" id="question" value="{{question}}" type="text" />
</div>
<span class="tip setting-help">Example: 'What is your favorite color?'</span>
</li>
</ul>
<div class="xblock-actions">
<ul>
<li class="action-item" id="poll-add-question">
<a href="#" class="button action-button" onclick="return false;">Add Question</a>
</li>
<li class="action-item">
<input type="submit" class="button action-primary save-button" value="Save" onclick="return false;" />
</li>
<li class="action-item">
<a href="#" class="button cancel-button">Cancel</a>
</li>
</ul>
</div>
</form>
</div>
\ No newline at end of file
......@@ -5,10 +5,8 @@ function PollBlock(runtime, element) {
var tallyURL = runtime.handlerUrl(element, 'get_results');
var submit = $('input[type=submit]', element);
var resultsTemplate = Handlebars.compile($("#results", element).html());
function getResults() {
function getResults(data) {
$.ajax({
// Semantically, this would be better as GET, but I can use helper
// functions with POST.
......@@ -16,9 +14,6 @@ function PollBlock(runtime, element) {
url: tallyURL,
data: JSON.stringify({}),
success: function (data) {
result = resultsTemplate(data);
console.log("Running...");
console.log(result);
element.innerHTML = resultsTemplate(data);
}
})
......@@ -27,6 +22,8 @@ function PollBlock(runtime, element) {
if (submit.length) {
var radios = $('input[name=choice]:checked', element);
submit.click(function (event) {
// Refresh.
radios = $(radios.selector);
var choice = radios.val();
console.log(choice);
$.ajax({
......
/* Javascript for ReferenceBlock. */
function PollEditBlock(runtime, element) {
var loadAnswers = runtime.handlerUrl(element, 'load_answers');
var temp = $('#answer-form-component', element).html();
var answerTemplate = Handlebars.compile(temp);
var pollLineItems =$('#poll-line-items', element);
// We probably don't need something this complex, but UUIDs are the
// standard.
function generateUUID(){
var d = new Date().getTime();
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = (d + Math.random()*16)%16 | 0;
d = Math.floor(d/16);
return (c=='x' ? r : (r&0x7|0x8)).toString(16);
})
}
function empowerDeletes() {
$('.poll-delete-answer', element).click(function () {
$(this).parent().remove();
});
}
function displayAnswers(data) {
pollLineItems.append(answerTemplate(data));
empowerDeletes();
}
$('#poll-add-question', element).click(function () {
pollLineItems.append(answerTemplate({'answers': [{'key': generateUUID(), 'text': ''}]}));
empowerDeletes();
pollLineItems.last().scrollTop();
});
$(element).find('.cancel-button').bind('click', function() {
runtime.notify('cancel', {});
});
$(element).find('.save-button').bind('click', function() {
var handlerUrl = runtime.handlerUrl(element, 'studio_submit');
var data = {};
$('#poll-form input', element).each(function() {
data[this.name] = this.value;
});
$.ajax({
type: "POST",
url: handlerUrl,
data: JSON.stringify(data),
success: function () {}
});
});
$(function ($) {
$.ajax({
type: "POST",
url: loadAnswers,
data: JSON.stringify({}),
success: displayAnswers
});
});
}
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
/* CSS for PollBlock */
.poll_block .count {
font-weight: bold;
}
.poll_block p {
cursor: pointer;
}
\ No newline at end of file
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