Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
555d524
Merge pull request #3 from open-craft/dashboard-pb
bradenmacdonald Apr 9, 2015
ea81a91
Removed custom deserialization code from questionnaire, tweaked tip d…
e-kolpakov Apr 7, 2015
45ca8c7
Added explicit test_requirements and included them in Travis build
e-kolpakov Apr 7, 2015
9ce9a4d
Merge reviewed removal of custom serialization code
bradenmacdonald Apr 9, 2015
552b5ba
Make travis work for all branches.
Kelketek Apr 7, 2015
ea2875f
Minor pylint fix
bradenmacdonald Apr 9, 2015
45072bb
Fix: Question titles were not visible in the LMS
bradenmacdonald Apr 10, 2015
3d4bf29
Fix: MCQ question text should be editable even if there are no choice…
bradenmacdonald Apr 10, 2015
8bf362d
Fix: Can't retry if max_attempts set to zero after answering while it…
bradenmacdonald Apr 10, 2015
0d85a3f
Fix: confusing behaviour of max_attempts when changing mode
bradenmacdonald Apr 10, 2015
dc4a43a
Fix: spacing around question titles/text in Firefox
bradenmacdonald Apr 10, 2015
4817f9d
Merge pull request #9 from open-craft/hotfix
bradenmacdonald Apr 16, 2015
94cc894
Hotfix: mentoring and problem_builder should not both use the same db…
bradenmacdonald Apr 23, 2015
7bb7d2b
Data migration
bradenmacdonald Apr 23, 2015
0cf055d
Presentation updates:
e-kolpakov Apr 21, 2015
b3cab3a
Merge pull request #16 from open-craft/presentation-updates
e-kolpakov Apr 24, 2015
d7ab348
Merge pull request #18 from open-craft/hotfix-oc-639
bradenmacdonald Apr 24, 2015
51a820e
Fix travis builds following xblock-sdk PR 56 change
bradenmacdonald Apr 24, 2015
5646645
Allowed overriding average labels per mentoring
e-kolpakov Apr 24, 2015
eb63e06
Merge pull request #19 from open-craft/presentation-updates
Kelketek Apr 24, 2015
7ea0c4e
Presentation updates, batch 2:
e-kolpakov May 5, 2015
60713fd
Accessibility updates - using sr and aria-hidden for better screen re…
e-kolpakov May 5, 2015
a4703a2
Dashboard export HTML updates
e-kolpakov May 5, 2015
e2f8c7a
Added ability to override feedback messages title
e-kolpakov May 5, 2015
e889b9b
Fixed tests and improved floaitng format output
e-kolpakov May 5, 2015
3523e75
Suppressed logging in tests - selenium driver messages flood the cons…
e-kolpakov May 5, 2015
45b3609
Fix Travis build
bradenmacdonald Apr 29, 2015
2b6e570
Merge pull request #29 from open-craft/presentation-updates
e-kolpakov May 7, 2015
85d12cc
Merge remote-tracking branch 'remotes/origin/edx-release' into merge-…
e-kolpakov May 12, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 109 additions & 16 deletions problem_builder/dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,14 @@
import json
import logging
import operator as op
from django.template.defaultfilters import floatformat

from .dashboard_visual import DashboardVisualData
from .mcq import MCQBlock
from .sub_api import sub_api
from lazy import lazy
from xblock.core import XBlock
from xblock.fields import Scope, List, String
from xblock.fields import Scope, List, String, Boolean, Dict
from xblock.fragment import Fragment
from xblock.validation import ValidationMessage
from xblockutils.helpers import child_isinstance
Expand Down Expand Up @@ -172,6 +173,20 @@ class DashboardBlock(StudioEditableXBlockMixin, XBlock):
).format(example_here='["2754b8afc03a439693b9887b6f1d9e36", "215028f7df3d4c68b14fb5fea4da7053"]'),
scope=Scope.settings,
)
exclude_questions = Dict(
display_name=_("Questions to be hidden"),
help=_(
"Optional rules to exclude specific questions both from displaying in dashboard and from the calculated "
"average. Rules must start with the url_name of a mentoring block, followed by list of question numbers "
"to exclude. Rule set must be in JSON format. Question numbers are one-based (the first question being "
"number 1). Must be in JSON format. Examples: {examples_here}"
).format(
examples_here='{"2754b8afc03a439693b9887b6f1d9e36":[1,2], "215028f7df3d4c68b14fb5fea4da7053":[1,5]}'
),
scope=Scope.content,
multiline_editor=True,
resettable_editor=False,
)
color_rules = String(
display_name=_("Color Coding Rules"),
help=_(
Expand Down Expand Up @@ -207,8 +222,27 @@ class DashboardBlock(StudioEditableXBlockMixin, XBlock):
),
scope=Scope.content,
)
average_labels = Dict(
display_name=_("Label for average value"),
help=_(
"This settings allows overriding label for the calculated average per mentoring block. Must be in JSON "
"format. Examples: {examples_here}."
).format(
examples_here='{"2754b8afc03a439693b9887b6f1d9e36": "Avg.", "215028f7df3d4c68b14fb5fea4da7053": "Mean"}'
),
scope=Scope.content,
)
show_numbers = Boolean(
display_name=_("Display values"),
default=True,
help=_("Toggles if numeric values are displayed"),
scope=Scope.content
)

editable_fields = ('display_name', 'mentoring_ids', 'color_rules', 'visual_rules', 'visual_title', 'visual_desc')
editable_fields = (
'display_name', 'mentoring_ids', 'exclude_questions', 'average_labels', 'show_numbers',
'color_rules', 'visual_rules', 'visual_title', 'visual_desc'
)
css_path = 'public/css/dashboard.css'
js_path = 'public/js/dashboard.js'

Expand Down Expand Up @@ -321,6 +355,12 @@ def _get_course_name(self):
except Exception:
return ""

def _get_problem_questions(self, mentoring_block):
""" Generator returning only children of specified block that are MCQs """
for child_id in mentoring_block.children:
if child_isinstance(mentoring_block, child_id, MCQBlock):
yield child_id

def student_view(self, context=None): # pylint: disable=unused-argument
"""
Standard view of this XBlock.
Expand All @@ -336,20 +376,36 @@ def student_view(self, context=None): # pylint: disable=unused-argument
'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 = self._get_submission_key(child_id)
try:
value = sub_api.get_submissions(mcq_submission_key, limit=1)[0]["answer"]
except IndexError:
value = None
block['mcqs'].append({
"display_name": mcq_block.display_name_with_default,
"value": value,
"color": self.color_for_value(value) if value is not None else None,
})
try:
hide_questions = self.exclude_questions.get(mentoring_block.url_name, [])
except Exception: # pylint: disable=broad-except-clause
log.exception("Cannot parse exclude_questions setting - probably malformed: %s", self.exclude_questions)
hide_questions = []

for question_number, child_id in enumerate(self._get_problem_questions(mentoring_block), 1):
try:
if question_number in hide_questions:
continue
except TypeError:
log.exception(
"Cannot check question number - expected list of ints got: %s",
hide_questions
)

# Get the student's submitted answer to this MCQ from the submissions API:
mcq_block = self.runtime.get_block(child_id)
mcq_submission_key = self._get_submission_key(child_id)
try:
value = sub_api.get_submissions(mcq_submission_key, limit=1)[0]["answer"]
except IndexError:
value = None

block['mcqs'].append({
"display_name": mcq_block.display_name_with_default,
"value": value,
"accessible_value": _("Score: {score}").format(score=value) if value else _("No value yet"),
"color": self.color_for_value(value) if value is not None else None,
})
# If the values are numeric, display an average:
numeric_values = [
float(mcq['value']) for mcq in block['mcqs']
Expand All @@ -358,6 +414,11 @@ def student_view(self, context=None): # pylint: disable=unused-argument
if numeric_values:
average_value = sum(numeric_values) / len(numeric_values)
block['average'] = average_value
# average block is shown only if average value exists, so accessible text for no data is not required
block['accessible_average'] = _("Score: {score}").format(
score=floatformat(average_value)
)
block['average_label'] = self.average_labels.get(mentoring_block.url_name, _("Average"))
block['has_average'] = True
block['average_color'] = self.color_for_value(average_value)
blocks.append(block)
Expand All @@ -384,6 +445,7 @@ def student_view(self, context=None): # pylint: disable=unused-argument
'blocks': blocks,
'display_name': self.display_name,
'visual_repr': visual_repr,
'show_numbers': self.show_numbers,
})

fragment = Fragment(html)
Expand All @@ -406,6 +468,37 @@ def add_error(msg):
except InvalidUrlName as e:
add_error(_(u'Invalid block url_name given: "{bad_url_name}"').format(bad_url_name=unicode(e)))

if data.exclude_questions:
for key, value in data.exclude_questions.iteritems():
if not isinstance(value, list):
add_error(
_(u"'Questions to be hidden' is malformed: value for key {key} is {value}, "
u"expected list of integers")
.format(key=key, value=value)
)

if key not in data.mentoring_ids:
add_error(
_(u"'Questions to be hidden' is malformed: mentoring url_name {url_name} "
u"is not added to Dashboard")
.format(url_name=key)
)

if data.average_labels:
for key, value in data.average_labels.iteritems():
if not isinstance(value, basestring):
add_error(
_(u"'Label for average value' is malformed: value for key {key} is {value}, expected string")
.format(key=key, value=value)
)

if key not in data.mentoring_ids:
add_error(
_(u"'Label for average value' is malformed: mentoring url_name {url_name} "
u"is not added to Dashboard")
.format(url_name=key)
)

if data.color_rules:
try:
self.parse_color_rules_str(data.color_rules, ignore_errors=False)
Expand Down
8 changes: 7 additions & 1 deletion problem_builder/mentoring.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@ class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioC
default=_("Mentoring Questions"),
scope=Scope.settings
)
feedback_label = String(
display_name=_("Feedback Header"),
help=_("Header for feedback messages"),
default=_("Feedback"),
scope=Scope.content
)

# User state
attempted = Boolean(
Expand Down Expand Up @@ -187,7 +193,7 @@ class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioC

editable_fields = (
'display_name', 'mode', 'followed_by', 'max_attempts', 'enforce_dependency',
'display_submit', 'weight', 'extended_feedback'
'display_submit', 'feedback_label', 'weight', 'extended_feedback'
)
icon_class = 'problem'
has_score = True
Expand Down
14 changes: 13 additions & 1 deletion problem_builder/public/css/dashboard.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
.pb-dashboard table {
max-width: 800px;
width: 700px;
table-layout: auto;
border-collapse: collapse;
margin-left: auto;
margin-right: auto;
margin-bottom: 15px;
}

Expand All @@ -9,6 +13,10 @@
font-weight: bold;
}

.pb-dashboard .avg-row .desc {
font-weight: 600;
}

.pb-dashboard table td, .pb-dashboard table tbody th {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
Expand All @@ -24,9 +32,13 @@
min-width: 4em;
text-align: right;
padding-right: 5px;
border-right: 0.6em solid transparent;
border-right: 2em solid transparent;
}

.pb-dashboard table .avg-row td.desc {
font-style: italic;
}

.pb-dashboard-visual {
text-align: center;
}
3 changes: 2 additions & 1 deletion problem_builder/public/js/mentoring.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ function MentoringBlock(runtime, element) {
hideAllSteps: hideAllSteps,
step: step,
steps: steps,
publish_event: publish_event
publish_event: publish_event,
data: data
};

function publish_event(data) {
Expand Down
2 changes: 1 addition & 1 deletion problem_builder/public/js/mentoring_standard_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ function MentoringStandardView(runtime, element, mentoring) {
// Messages should only be displayed upon hitting 'submit', not on page reload
mentoring.setContent(messagesDOM, response.message);
if (messagesDOM.html().trim()) {
messagesDOM.prepend('<div class="title1">' + gettext('Feedback') + '</div>');
messagesDOM.prepend('<div class="title1">' + mentoring.data.feedback_label + '</div>');
messagesDOM.show();
}
}
Expand Down
16 changes: 11 additions & 5 deletions problem_builder/templates/html/dashboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,22 @@ <h2>{{display_name}}</h2>
{% for mcq in block.mcqs %}
<tr>
<th class="desc">{{ mcq.display_name }}</th>
<td class="value" {% if mcq.color %}style="border-right-color: {{mcq.color}};"{% endif %}>
{% if mcq.value %}{{ mcq.value }}{% endif %}
<td class="value" {% if mcq.color %} style="border-right-color: {{mcq.color}};"{% endif %}>
{% if mcq.value and show_numbers %}
<span aria-hidden="true">{{ mcq.value }}</span>
{% endif %}
<span class="sr">{{ mcq.accessible_value }}</span>
</td>
</tr>
{% endfor %}
{% if block.has_average %}
<tr class="avg-row">
<th class="desc">{% trans "Average" %}</th>
<td class="value" {% if block.average_color %}style="border-right-color: {{block.average_color}};"{% endif %}>
{{ block.average|floatformat }}
<th class="desc">{{ block.average_label }}</th>
<td class="value" {% if block.average_color %} style="border-right-color: {{block.average_color}};"{% endif %}>
{% if show_numbers %}
<span aria-hidden="true">{{ block.average|floatformat }}</span>
{% endif %}
<span class="sr">{{ block.accessible_average }}</span>
</td>
</tr>
{% endif %}
Expand Down
14 changes: 14 additions & 0 deletions problem_builder/templates/html/dashboard_report.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,20 @@
body {
font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
.pb-dashboard table {
text-align: left;
}
/* screen reader class from edx-platform */
.sr {
border: 0;
clip: rect(1px 1px 1px 1px);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
{{css}}
</style>
</head>
Expand Down
2 changes: 1 addition & 1 deletion problem_builder/templates/html/mentoring.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% load i18n %}
<div class="mentoring themed-xblock" data-mode="{{ self.mode }}" data-step="{{ self.step }}">
<div class="mentoring themed-xblock" data-mode="{{ self.mode }}" data-step="{{ self.step }}" data-feedback_label="{{ self.feedback_label}}">
<div class="missing-dependency warning" data-missing="{{ self.has_missing_dependency }}">
{% with url=missing_dependency_url|safe %}
{% blocktrans with link_start="<a href='"|add:url|add:"'>" link_end="</a>" %}
Expand Down
Loading