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
"""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