{}
".format(self.display_name, _("Not configured."))) + + blocks = [] + for mentoring_block in self.get_mentoring_blocks(self.mentoring_ids): + 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 = 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, + }) + # If the values are numeric, display an average: + numeric_values = [ + float(mcq['value']) for mcq in block['mcqs'] + if mcq['value'] is not None and mcq['value'].isnumeric() + ] + if numeric_values: + average_value = sum(numeric_values) / len(numeric_values) + block['average'] = average_value + block['has_average'] = True + block['average_color'] = self.color_for_value(average_value) + blocks.append(block) + + visual_repr = None + if self.visual_rules: + try: + rules_parsed = json.loads(self.visual_rules) + except ValueError: + pass # JSON errors should be shown as part of validation + else: + visual_repr = DashboardVisualData( + blocks, rules_parsed, self.color_for_value, self.visual_title, self.visual_desc + ) + + report_template = loader.render_template('templates/html/dashboard_report.html', { + 'title': self.display_name, + 'css': loader.load_unicode(self.css_path), + 'student_name': self._get_user_full_name(), + 'course_name': self._get_course_name(), + }) + + html = loader.render_template('templates/html/dashboard.html', { + 'blocks': blocks, + 'display_name': self.display_name, + 'visual_repr': visual_repr, + }) + + fragment = Fragment(html) + fragment.add_css_url(self.runtime.local_resource_url(self, self.css_path)) + fragment.add_javascript_url(self.runtime.local_resource_url(self, self.js_path)) + fragment.initialize_js('PBDashboardBlock', {'reportTemplate': report_template}) + return fragment + + def validate_field_data(self, validation, data): + """ + Validate this block's field data. + """ + super(DashboardBlock, self).validate_field_data(validation, data) + + def add_error(msg): + validation.add(ValidationMessage(ValidationMessage.ERROR, msg)) + + try: + list(self.get_mentoring_blocks(data.mentoring_ids, ignore_errors=False)) + except InvalidUrlName as e: + add_error(_(u'Invalid block url_name given: "{bad_url_name}"').format(bad_url_name=unicode(e))) + + if data.color_rules: + try: + self.parse_color_rules_str(data.color_rules, ignore_errors=False) + except ValueError as e: + add_error(unicode(e)) + + if data.visual_rules: + try: + rules = json.loads(data.visual_rules) + except ValueError as e: + add_error(_(u"Visual rules contains an error: {error}").format(error=e)) + else: + if not isinstance(rules, dict): + add_error(_(u"Visual rules should be a JSON dictionary/object: {...}")) diff --git a/problem_builder/dashboard_visual.py b/problem_builder/dashboard_visual.py new file mode 100644 index 00000000..4e5e51b7 --- /dev/null +++ b/problem_builder/dashboard_visual.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2014-2015 Harvard, edX & OpenCraft +# +# This software's license gives you freedom; you can copy, convey, +# propagate, redistribute and/or modify this program under the terms of +# the GNU Affero General Public License (AGPL) as published by the Free +# Software Foundation (FSF), either version 3 of the License, or (at your +# option) any later version of the AGPL published by the FSF. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero +# General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program in a file in the toplevel directory called +# "AGPLv3". If not, see| {{ block.display_name }} | + + + {% for mcq in block.mcqs %} +|
|---|---|
| {{ mcq.display_name }} | ++ {% if mcq.value %}{{ mcq.value }}{% endif %} + | +
| {% trans "Average" %} | ++ {{ block.average|floatformat }} + | +