Commit 70d645fd by Jonathan Piacenti

First Prototype.

parent fe27bd5f
# Created by https://www.gitignore.io
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.cache
nosetests.xml
coverage.xml
# Translations
*.mo
*.pot
# Django stuff:
*.log
# Sphinx documentation
docs/_build/
# PyBuilder
target/
### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
/*.iml
## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:
# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries
# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml
# Gradle:
# .idea/gradle.xml
# .idea/libraries
# Mongo Explorer plugin:
# .idea/mongoSettings.xml
## File-based project format:
*.ipr
*.iws
## Plugin-specific files:
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
### vim ###
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
*.un~
Session.vim
.netrwhist
*~
### Linux ###
*~
# KDE directory preferences
.directory
from .poll import PollBlock
\ No newline at end of file
"""TO-DO: Write a description of what this XBlock is."""
from django.template import Template, Context
import pkg_resources
from xblock.core import XBlock
from xblock.fields import Scope, List, Integer, String, Dict
from xblock.fragment import Fragment
class PollBlock(XBlock):
"""
Poll XBlock. Allows a teacher to poll users, and presents the results so
far of the poll to the user when finished.
"""
question = String(default='What is your favorite color?')
answers = List(
default=['Red', 'Blue', 'Green', '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,
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")
def resource_string(self, path):
"""Handy helper for getting resources from our kit."""
data = pkg_resources.resource_string(__name__, path)
return data.decode("utf8")
@XBlock.json_handler
def get_results(self, data, suffix=''):
return {'question': self.question, 'tally': self.tally_detail()}
def tally_detail(self):
"""
Tally all results.
"""
# TODO: Cache this.
tally = [{'count': 0, 'answer': answer, 'top': False}
for answer in 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 answer in tally:
try:
percent = (answer['count'] / float(total))
answer['percent'] = int(percent * 100)
except ZeroDivisionError:
answer['percent'] = 0
tally.sort(key=lambda x: x['count'], reverse=True)
# This should always be true, but on the off chance there are
# no answers...
if tally:
# Mark the top item to make things easier for Handlebars.
tally[0]['top'] = True
return tally
# TO-DO: change this view to display your data your own way.
def student_view(self, context=None):
"""
The primary view of the PollBlock, shown to students
when viewing courses.
"""
if not context:
context = Context()
js_template = self.resource_string(
'/static/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),
'question': self.question,
'js_template': js_template,
})
if self.choice:
context.update({'tally': self.tally_detail()})
html = self.resource_string("static/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.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 vote(self, data, suffix=''):
"""
An example handler, which increments the data.
"""
result = {'result': 'error'}
if self.choice is not None:
result['message'] = 'You cannot vote twice.'
return result
try:
choice = data['choice']
except KeyError:
result['message'] = 'Answer not included with request.'
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)
return result
self.choice = choice
self.tally[choice - 1] += 1
result['result'] = 'success'
return result
# TO-DO: change this to create the scenarios you'd like to see in the
# workbench while developing your XBlock.
@staticmethod
def workbench_scenarios():
"""A canned scenario for display in the workbench."""
return [
("PollBlock",
"""<vertical_demo>
<poll/>
</vertical_demo>
"""),
]
\ No newline at end of file
This static directory is for files that should be included in your kit as plain
static files.
You can ask the runtime for a URL that will retrieve these files with:
url = self.runtime.local_resource_url(self, "static/js/lib.js")
The default implementation is very strict though, and will not serve files from
the static directory. It will serve files from a directory named "public".
Create a directory alongside this one named "public", and put files there.
Then you can get a url with code like this:
url = self.runtime.local_resource_url(self, "public/js/lib.js")
The sample code includes a function you can use to read the content of files
in the static directory, like this:
frag.add_javascript(self.resource_string("static/js/my_block.js"))
/* CSS for PollBlock */
.poll_block .count {
font-weight: bold;
}
.poll_block p {
cursor: pointer;
}
\ No newline at end of file
<script id="results" type="text/html">
<h2>{{question}}</h2>
<ul>
{{#each tally}}
<li class="poll-result">
<span class="percentage-guage" style="width:{{percentage}}%;"></span>
<span>{{answer}}</span>
<span class="poll-percent-display{{#if top}} poll-top-choice{{/if}}">{{percent}}%</span>
</li>
{{/each}}
</ul>
</script>
\ No newline at end of file
{{ js_template|safe }}
<div class="poll_block">
{# If no form is present, the Javascript will load the results instead. #}
{% if not choice %}
<form>
<h2>{{ question }}</h2>
<ul>
{% for number, answer in answers %}
<li class="poll-answer">
<label for="answer-{{number}}">{{answer}}</label>
<input type="radio" name="choice" id="answer-{{number}}" value="{{number}}">
</li>
{% endfor %}
</ul>
<input type="submit" name="poll-submit" onclick="return false;">
</form>
{% endif %}
</div>
\ No newline at end of file
/* Javascript for PollBlock. */
function PollBlock(runtime, element) {
var voteUrl = runtime.handlerUrl(element, 'vote');
var tallyURL = runtime.handlerUrl(element, 'get_results');
var submit = $('input[type=submit]', element);
var resultsTemplate = Handlebars.compile($("#results", element).html());
function getResults() {
$.ajax({
// Semantically, this would be better as GET, but I can use helper
// functions with POST.
type: "POST",
url: tallyURL,
data: JSON.stringify({}),
success: function (data) {
result = resultsTemplate(data);
console.log("Running...");
console.log(result);
element.innerHTML = resultsTemplate(data);
}
})
}
if (submit.length) {
var radios = $('input[name=choice]:checked', element);
submit.click(function (event) {
var choice = radios.val();
console.log(choice);
$.ajax({
type: "POST",
url: voteUrl,
data: JSON.stringify({"choice": choice}),
success: getResults
});
});
var answers = $('li', element);
function enableSubmit() {
submit.removeAttr("disabled");
answers.unbind("change.EnableSubmit");
}
if (! radios.val()) {
answers.bind("change.EnableSubmit", enableSubmit);
} else {
enableSubmit();
}
} else {
getResults();
}
$(function ($) {
});
}
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
"""Setup for poll XBlock."""
import os
from setuptools import setup
def package_data(pkg, roots):
"""Generic function to find package_data.
All of the files under each of the `roots` will be declared as package
data for package `pkg`.
"""
data = []
for root in roots:
for dirname, _, files in os.walk(os.path.join(pkg, root)):
for fname in files:
data.append(os.path.relpath(os.path.join(dirname, fname), pkg))
return {pkg: data}
setup(
name='poll-xblock',
version='0.1',
description='poll XBlock', # TODO: write a better description.
packages=[
'poll',
],
install_requires=[
'XBlock',
],
entry_points={
'xblock.v1': [
'poll = poll:PollBlock',
]
},
package_data=package_data("poll", ["static", "public"]),
)
\ 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