Commit bee4e702 by Jonathan Piacenti

Allow students to download a copy of their answer table.

parent 6aaa3aba
......@@ -272,6 +272,8 @@ class AnswerRecapBlock(AnswerMixin, StudioEditableXBlockMixin, XBlock):
)
editable_fields = ('name', 'display_name', 'description')
css_path = 'public/css/answer.css'
@property
def student_input(self):
if self.name:
......@@ -287,7 +289,7 @@ class AnswerRecapBlock(AnswerMixin, StudioEditableXBlockMixin, XBlock):
html = loader.render_template('templates/html/answer_read_only.html', context)
fragment = Fragment(html)
fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/answer.css'))
fragment.add_css_url(self.runtime.local_resource_url(self, self.css_path))
return fragment
def student_view(self, context=None):
......
......@@ -58,6 +58,39 @@ def _(text):
# Classes ###########################################################
class ExportMixin(object):
"""
Used by blocks which need to provide a downloadable export.
"""
def _get_user_full_name(self):
"""
Get the full name of the current user, for the downloadable report.
"""
user_service = self.runtime.service(self, 'user')
if user_service:
return user_service.get_current_user().full_name
return ""
def _get_course_name(self):
"""
Get the name of the current course, for the downloadable report.
"""
try:
course_key = self.scope_ids.usage_id.course_key
except AttributeError:
return "" # We are not in an edX runtime
try:
course_root_key = course_key.make_usage_key('course', 'course')
return self.runtime.get_block(course_root_key).display_name
except Exception: # ItemNotFoundError most likely, but we can't import that exception in non-edX environments
# We may be on old mongo:
try:
course_root_key = course_key.make_usage_key('course', course_key.run)
return self.runtime.get_block(course_root_key).display_name
except Exception:
return ""
class ColorRule(object):
"""
A rule used to conditionally set colors
......@@ -155,7 +188,7 @@ class InvalidUrlName(ValueError):
@XBlock.needs("i18n")
@XBlock.wants("user")
class DashboardBlock(StudioEditableXBlockMixin, XBlock):
class DashboardBlock(StudioEditableXBlockMixin, ExportMixin, XBlock):
"""
A block to summarize self-assessment results.
"""
......@@ -260,7 +293,7 @@ class DashboardBlock(StudioEditableXBlockMixin, XBlock):
'color_rules', 'visual_rules', 'visual_title', 'visual_desc', 'header_html', 'footer_html',
)
css_path = 'public/css/dashboard.css'
js_path = 'public/js/dashboard.js'
js_path = 'public/js/review_blocks.js'
def get_mentoring_blocks(self, mentoring_ids, ignore_errors=True):
"""
......@@ -343,34 +376,6 @@ class DashboardBlock(StudioEditableXBlockMixin, XBlock):
return rule.color_str
return None
def _get_user_full_name(self):
"""
Get the full name of the current user, for the downloadable report.
"""
user_service = self.runtime.service(self, 'user')
if user_service:
return user_service.get_current_user().full_name
return ""
def _get_course_name(self):
"""
Get the name of the current course, for the downloadable report.
"""
try:
course_key = self.scope_ids.usage_id.course_key
except AttributeError:
return "" # We are not in an edX runtime
try:
course_root_key = course_key.make_usage_key('course', 'course')
return self.runtime.get_block(course_root_key).display_name
except Exception: # ItemNotFoundError most likely, but we can't import that exception in non-edX environments
# We may be on old mongo:
try:
course_root_key = course_key.make_usage_key('course', course_key.run)
return self.runtime.get_block(course_root_key).display_name
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:
......@@ -469,7 +474,11 @@ class DashboardBlock(StudioEditableXBlockMixin, XBlock):
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})
fragment.initialize_js(
'PBDashboardBlock', {
'reportTemplate': report_template,
'reportContentSelector': '.dashboard-report'
})
return fragment
def validate_field_data(self, validation, data):
......
function MentoringTableBlock(runtime, element) {
// Display an exceprt for long answers, with a "more" link to display the full text
$('.answer-table', element).shorten({
moreText: 'more',
lessText: 'less',
showChars: '500'
});
return {};
}
// Client side code for the Problem Builder Dashboard XBlock
// So far, this code is only used to generate a downloadable report.
function PBDashboardBlock(runtime, element, initData) {
function ExportBase(runtime, element, initData) {
"use strict";
var reportTemplate = initData.reportTemplate;
......@@ -32,7 +32,7 @@ function PBDashboardBlock(runtime, element, initData) {
// Download Report:
// Change the URL to a data: URI before continuing with the click event.
if ($(this).attr('href').charAt(0) == '#') {
var $report = $('.dashboard-report', element).clone();
var $report = $(initData.reportContentSelector, element).clone();
// Convert all images in $report to data URIs:
$report.find('image').each(function() {
var origURL = $(this).attr('xlink:href');
......@@ -49,3 +49,17 @@ function PBDashboardBlock(runtime, element, initData) {
var $downloadLink = $('.report-download-link', element);
$downloadLink.on('click', downloadReport);
}
function PBDashboardBlock(runtime, element, initData) {
new ExportBase(runtime, element, initData);
}
function MentoringTableBlock(runtime, element, initData) {
// Display an excerpt for long answers, with a "more" link to display the full text
$('.answer-table', element).shorten({
moreText: 'more',
lessText: 'less',
showChars: '500'
});
new ExportBase(runtime, element, initData)
}
......@@ -24,13 +24,15 @@ import errno
from xblock.core import XBlock
from xblock.fields import Scope, String
from xblock.fields import Scope, String, Boolean
from xblock.fragment import Fragment
from xblockutils.resources import ResourceLoader
from xblockutils.studio_editable import StudioEditableXBlockMixin, StudioContainerXBlockMixin
# Globals ###########################################################
from problem_builder import AnswerRecapBlock
from problem_builder.dashboard import ExportMixin
loader = ResourceLoader(__name__)
......@@ -42,7 +44,8 @@ def _(text):
# Classes ###########################################################
class MentoringTableBlock(StudioEditableXBlockMixin, StudioContainerXBlockMixin, XBlock):
@XBlock.wants("user")
class MentoringTableBlock(StudioEditableXBlockMixin, StudioContainerXBlockMixin, ExportMixin, XBlock):
"""
Table-type display of information from mentoring blocks
......@@ -66,9 +69,18 @@ class MentoringTableBlock(StudioEditableXBlockMixin, StudioContainerXBlockMixin,
{"display_name": "Immunity Map", "value": "immunity-map"},
],
)
editable_fields = ("type", )
editable_fields = ("type", "allow_download")
allow_download = Boolean(
display_name=_("Allow Download"),
help=_("Allow students to download a copy of the table for themselves."),
default=False,
scope=Scope.content
)
has_children = True
css_path = 'public/css/mentoring-table.css'
js_path = 'public/js/review_blocks.js'
def student_view(self, context):
context = context.copy() if context else {}
fragment = Fragment()
......@@ -83,6 +95,7 @@ class MentoringTableBlock(StudioEditableXBlockMixin, StudioContainerXBlockMixin,
fragment.add_frag_resources(child_frag)
context['header_values'] = header_values if any(header_values) else None
context['content_values'] = content_values
context['allow_download'] = self.allow_download
if self.type:
# Load an optional background image:
......@@ -96,11 +109,23 @@ class MentoringTableBlock(StudioEditableXBlockMixin, StudioContainerXBlockMixin,
else:
raise
report_template = loader.render_template('templates/html/mentoring-table-report.html', {
'title': self.display_name,
'css': loader.load_unicode(AnswerRecapBlock.css_path) + loader.load_unicode(self.css_path),
'student_name': self._get_user_full_name(),
'course_name': self._get_course_name(),
})
fragment.add_content(loader.render_template('templates/html/mentoring-table.html', context))
fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/mentoring-table.css'))
fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/vendor/jquery-shorten.js'))
fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/mentoring-table.js'))
fragment.initialize_js('MentoringTableBlock')
fragment.add_javascript_url(self.runtime.local_resource_url(self, self.js_path))
fragment.initialize_js(
'MentoringTableBlock', {
'reportContentSelector': '.mentoring-table-container',
'reportTemplate': report_template,
}
)
return fragment
......
{% load i18n %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ title }}</title>
<style>
body {
font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
{{css|safe}}
</style>
</head>
<body>
<div class="mentoring">
<div class="identification">
{% if student_name %}{% trans "Student" %}: {{student_name}}<br>{% endif %}
{% if course_name %}{% trans "Course" %}: {{course_name}}<br>{% endif %}
{% trans "Date" %}: {% now "DATE_FORMAT" %}<br>
</div>
REPORT_GOES_HERE
</div>
</body>
</html>
<div class="mentoring-table {{ self.type }}" style="background-image: url({{ bg_image_url }})">
<div class="cont-text-sr">{{ bg_image_description }}</div>
<table>
{% if header_values %}
<thead>
{% for header in header_values %}
<th>{{ header|safe }}</th>
{% endfor %}
</thead>
{% endif %}
<tbody>
<tr>
{% for content in content_values %}
<td>
<div class="mentoring-column">
{{content|safe}}
</div>
</td>
{% load i18n %}
<div class="mentoring-table-container">
<div class="mentoring-table {{ self.type }}" style="background-image: url({{ bg_image_url }})">
<div class="cont-text-sr">{{ bg_image_description }}</div>
<table>
{% if header_values %}
<thead>
{% for header in header_values %}
<th>{{ header|safe }}</th>
{% endfor %}
</tr>
</tbody>
</table>
</div>
</thead>
{% endif %}
<tbody>
<tr>
{% for content in content_values %}
<td>
<div class="mentoring-column">
{{content|safe}}
</div>
</td>
{% endfor %}
</tr>
</tbody>
</table>
</div>
{% if allow_download %}
<p><a class="report-download-link" href="#report_download" download="report.html">{% trans "Download report" %}</a></p>
{% endif %}
</div>
\ 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