Commit 8633044e by Braden MacDonald

Implementation of core features

parent 7a3423dc
......@@ -21,19 +21,23 @@
# Imports ###########################################################
import logging
from lazy import lazy
from .mcq import MCQBlock
from .sub_api import sub_api
from xblock.core import XBlock
from xblock.fields import Scope, String
from xblock.exceptions import XBlockNotFoundError
from xblock.fields import Scope, Dict, List, String
from xblock.fragment import Fragment
from xblock.validation import ValidationMessage
from xblockutils.helpers import child_isinstance
from xblockutils.resources import ResourceLoader
from xblockutils.studio_editable import StudioEditableXBlockMixin
# Globals ###########################################################
log = logging.getLogger(__name__)
loader = ResourceLoader(__name__)
# Make '_' a no-op so we can scrape strings
......@@ -54,35 +58,82 @@ class DashboardBlock(StudioEditableXBlockMixin, XBlock):
scope=Scope.settings,
default=_('Self-Assessment Summary'),
)
mcq_id = String(
display_name=_("MCQ ID"),
help=_("THe url_name of an MCQ to use as a proof of concept for using the submissions API"),
mentoring_ids = List(
display_name=_("Mentoring Blocks"),
help=_(
"This should be an ordered list of the url_names of each mentoring block whose multiple choice question "
"values are to be shown on this dashboard. The list should be in JSON format. Example: {example_here}"
).format(example_here='["2754b8afc03a439693b9887b6f1d9e36", "215028f7df3d4c68b14fb5fea4da7053"]'),
scope=Scope.content,
default=""
)
color_codes = Dict(
display_name=_("Color Coding"),
help=_(
"You can optionally set a color for each expected value. Example: {example_here}"
).format(example_here='{ "1": "red", "2": "yellow", 3: "green", 4: "lightskyblue" }')
)
editable_fields = ('display_name', 'mentoring_ids', 'color_codes')
def get_mentoring_blocks(self):
"""
Generator returning the specified mentoring blocks, in order.
returns a list. Will insert None for every invalid mentoring block ID.
"""
for url_name in self.mentoring_ids:
mentoring_id = self.scope_ids.usage_id.course_key.make_usage_key('problem-builder', url_name)
try:
yield self.runtime.get_block(mentoring_id)
except XBlockNotFoundError:
yield None
def generate_content(self):
"""
Create the HTML for this block, by getting the data and inserting it into a template.
"""
blocks = []
for mentoring_block in self.get_mentoring_blocks():
if mentoring_block is None:
continue
block = {
'display_name': mentoring_block.display_name,
'mcqs': []
}
for child_id in mentoring_block.children:
if child_isinstance(mentoring_block, child_id, MCQBlock):
# Get the student's submitted answer to this MCQ from the submissions API:
mcq_block = self.runtime.get_block(child_id)
mcq_submission_key = dict(
student_id=self.runtime.anonymous_student_id,
course_id=unicode(child_id.course_key),
item_id=unicode(child_id),
item_type=child_id.block_type,
)
try:
value = sub_api.get_submissions(mcq_submission_key, limit=1)[0]["answer"]
except IndexError: # (sub_api.SubmissionNotFoundError, IndexError)
value = None
block['mcqs'].append({
"display_name": mcq_block.question,
"value": value,
"color": self.color_codes.get(value),
})
blocks.append(block)
editable_fields = ('display_name', 'mcq_id', )
return loader.render_template('templates/html/dashboard.html', {
'blocks': blocks,
'display_name': self.display_name,
})
def fallback_view(self, view_name, context=None):
context = context or {}
context['self'] = self
if self.mcq_id:
item_usage_key = self.scope_ids.usage_id.course_key.make_usage_key('pb-mcq', self.mcq_id)
item_key = dict(
student_id=self.runtime.anonymous_student_id,
course_id=unicode(item_usage_key.course_key),
item_id=unicode(item_usage_key),
item_type=item_usage_key.block_type,
)
try:
submission = sub_api.get_submissions(item_key, limit=1)[0]
except (sub_api.SubmissionNotFoundError, IndexError):
message = "(No answer submitted yet)"
else:
message = "Answer is: {}.".format(submission["answer"])
else:
message = "(Not configured)"
fragment = Fragment(u"<h1>Dashboard</h1><p>{}</p>".format(message))
return fragment
if not self.mentoring_ids:
return Fragment(u"<h1>{}</h1><p>{}</p>".format(self.display_name, _("Not configured.")))
fragment = Fragment(self.generate_content())
fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/dashboard.css'))
return fragment
\ No newline at end of file
.pb-dashboard table {
max-width: 800px;
border-collapse: collapse;
}
.pb-dashboard table td {
border: 1px solid black;
padding: 0.2em;
}
.pb-dashboard table td:last-child {
min-width: 3em;
text-align: right;
}
.pb-dashboard table td:last-child span {
position: relative;
background: transparent;
color: black;
/* Background color could be anything, so add a white outline to preserve readability */
text-shadow:
-1px -1px 1px rgba(255,255,255,0.7),
1px -1px 1px rgba(255,255,255,0.7),
-1px 1px 1px rgba(255,255,255,0.7),
1px 1px 1px rgba(255,255,255,0.7);
}
<div class="pb-dashboard">
<h1>{{display_name}}</h1>
<table>
{% for block in blocks %}
<thead>
<th colspan=2>{{ block.display_name }}</th>
</thead>
<tbody>
{% for mcq in block.mcqs %}
<tr>
<td>{{ mcq.display_name }}</td>
<td {% if mcq.color %}style="background-color: {{mcq.color}};"{% endif %}>
<span>{% if mcq.value %}{{ mcq.value }}{% endif %}</span>
</td>
</tr>
{% endfor %}
</tbody>
{% endfor %}
</table>
</div>
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