Commit f4638973 by Tim Krones

Rework UI of Student Answers Dashboard.

parent e0f62ff8
.data-export-options, .data-export-results {
.data-export-options, .data-export-results, .data-export-status {
margin-top: 2em;
}
.data-export-actions button {
display: none;
.data-export-options, .data-export-results table {
border: 2px solid #999;
}
.data-export-status {
margin-top: 2em;
height: 8em;
.data-export-options, .data-export-results thead {
background-color: #ddd;
}
.data-export-options {
display: table;
padding: 1em;
}
.data-export-header, .data-export-row {
display: table-row;
}
.data-export-header h3, .data-export-results thead {
font-weight: bold;
}
.data-export-header h3 {
margin-top: 0px;
margin-bottom: 10px;
}
.data-export-field-container, .data-export-options .data-export-actions {
display: table-cell;
padding-left: 1em;
}
.data-export-field {
margin-top: .5em;
margin-bottom: .5em;
}
.data-export-field label span {
font-weight: bold;
}
.data-export-helptext {
font-size: 75%;
padding-right: .5em;
vertical-align: middle;
}
.data-export-field-container {
margin-bottom: 1em;
.data-export-field input, .data-export-field select {
max-width: 60%;
float: right;
}
.data-export-results {
.data-export-results, .data-export-download, .data-export-cancel, .data-export-delete {
display: none;
}
.data-export-results table {
margin-top: 1em;
border: 2px solid gray;
}
.data-export-results thead {
border-bottom: 2px solid gray;
font-weight: bold;
border-bottom: 2px solid #999;
}
.data-export-results td {
border-left: 1px solid #999;
padding: 5px;
}
.data-export-results .even {
background-color: #eee;
}
.data-export-info p {
font-size: 75%;
}
.data-export-status {
margin-bottom: 1em;
}
.data-export-status i {
font-size: 3em;
}
.data-export-actions {
text-align: right;
}
......@@ -44,7 +44,7 @@ function StudentAnswersDashboardBlock(runtime, element) {
$deleteButton.prop('disabled', true);
$('.data-export-status', $element).empty().append(
$('<i>').addClass('icon fa fa-spinner fa-spin')
);
).css("text-align", "center");
}
function hideResults() {
......@@ -68,8 +68,10 @@ function StudentAnswersDashboardBlock(runtime, element) {
}
function updateView() {
var $statusArea = $('.data-export-status', $element), startTime;
var $exportInfo = $('.data-export-info', $element),
$statusArea = $('.data-export-status', $element), startTime;
$statusArea.empty();
$exportInfo.empty();
$startButton.toggle(!status.export_pending).prop('disabled', false);
$cancelButton.toggle(status.export_pending).prop('disabled', false);
$downloadButton.toggle(Boolean(status.download_url)).prop('disabled', false);
......@@ -82,16 +84,14 @@ function StudentAnswersDashboardBlock(runtime, element) {
{'error': status.last_export_result.error}
)
));
hideResults();
} else {
startTime = new Date(status.last_export_result.start_timestamp * 1000);
$statusArea.append($('<p>').text(
gettext('A report is available for download.')
));
$statusArea.append($('<p>').text(
$exportInfo.append($('<p>').text(
_.template(
ngettext(
'It was created at <%= creation_time %> and took <%= seconds %> second to finish.',
'It was created at <%= creation_time %> and took <%= seconds %> seconds to finish.',
'Results retrieved on <%= creation_time %> (<%= seconds %> second).',
'Results retrieved on <%= creation_time %> (<%= seconds %> seconds).',
status.last_export_result.generation_time_s.toFixed(1)
),
{
......@@ -100,35 +100,27 @@ function StudentAnswersDashboardBlock(runtime, element) {
}
)
));
}
// Display results
var $resultTableBody = $resultTable.find('tbody');
$resultTableBody.empty();
_.each(status.last_export_result.display_data, function(row) {
var tr = $('<tr>');
_.each(row, function(cell) {
tr.append($('<td>').text(cell));
// Display results
var $resultTableBody = $resultTable.find('tbody');
$resultTableBody.empty();
_.each(status.last_export_result.display_data, function(row, index) {
var tr = $('<tr>');
if (index % 2 === 0) {
tr.addClass('even');
}
_.each(row, function(cell) {
tr.append($('<td>').text(cell));
});
$resultTableBody.append(tr);
});
$resultTableBody.append(tr);
});
showResults();
showResults();
}
} else {
if (status.export_pending) {
$statusArea.append($('<p>').text(
gettext('The report is currently being generated…')
));
} else {
$statusArea.append($('<p>').text(
gettext('No report data available.')
));
}
}
}
......
......@@ -164,20 +164,16 @@ class StudentAnswersDashboardBlock(XBlock):
username = data.get('username', None)
root_block_id = data.get('root_block_id', None)
match_string = data.get('match_string', None)
if not root_block_id:
root_block_id = self.scope_ids.usage_id
# Block ID not in workbench runtime.
root_block_id = unicode(getattr(root_block_id, 'block_id', root_block_id))
get_root = True
# Process user-submitted data
if block_types == 'all':
block_types = []
else:
get_root = False
block_types = [block_types]
user_service = self.runtime.service(self, 'user')
if not self.user_is_staff():
return {'error': 'permission denied'}
from .tasks import export_data as export_data_task # Import here since this is edX LMS specific
self._delete_export()
# Make sure we nail down our state before sending off an asynchronous task.
self.save()
if not username:
user_id = None
else:
......@@ -185,6 +181,19 @@ class StudentAnswersDashboardBlock(XBlock):
if user_id is None:
self.raise_error(404, _("Could not find the specified username."))
if not root_block_id:
root_block_id = self.scope_ids.usage_id
# Block ID not in workbench runtime.
root_block_id = unicode(getattr(root_block_id, 'block_id', root_block_id))
get_root = True
else:
get_root = False
# Launch task
from .tasks import export_data as export_data_task # Import here since this is edX LMS specific
self._delete_export()
# Make sure we nail down our state before sending off an asynchronous task.
self.save()
async_result = export_data_task.delay(
# course_id not available in workbench.
unicode(getattr(self.runtime, 'course_id', 'course_id')),
......
{% load i18n %}
<h3>{% trans "Student Answers Dashboard" %}</h3>
<p>{% trans "You can export all student answers to multiple-choice questions and long-form answers to a CSV file here." %}</p>
<h2>{% trans "Student Answers Dashboard" %}</h3>
<div class="data-export-options">
<div class="data-export-field-container">
<div class="data-export-field">
<label>
<span>{% trans "Problem types:" %}</span>
<select multiple name="block_types">
{% for label, value in block_choices.items %}
<option value="{{value}}">{{label}}</option>
{% endfor %}
</select>
</label>
</div>
<div class="data-export-helptext">
{% trans "Select which types of problem to include (selecting none will grab all types)" %}
</div>
<div class="data-export-header">
<h3>{% trans "Filters" %}</h3>
</div>
<div class="data-export-field-container">
<div class="data-export-field">
<label>
<span>{% trans "Root block ID:" %}</span>
<input type="text" name="root_block_id" />
</label>
<div class="data-export-row">
<div class="data-export-field-container">
<div class="data-export-field">
<label>
<span>{% trans "Username:" %}</span>
<input type="text" name="username" />
</label>
</div>
</div>
<div class="data-export-helptext">
{% trans "Input the ID of a chapter, section, or unit if you wish to only get results under it. Otherwise, it will grab all results for the course." %}
<div class="data-export-field-container">
<div class="data-export-field">
<label>
<span>{% trans "Text:" %}</span>
<input type="text" name="match_string" />
</label>
</div>
</div>
</div>
<div class="data-export-field-container">
<div class="data-export-field">
<label>
<span>{% trans "Username:" %}</span>
<input type="text" name="username" />
</label>
<div class="data-export-row">
<div class="data-export-field-container">
<div class="data-export-field">
<label>
<span>{% trans "Root block ID:" %}</span>
<input type="text" name="root_block_id" />
</label>
</div>
</div>
<div class="data-export-helptext">
{% trans "Input the username of a student if you wish to query for a specific one. Otherwise, it will grab all results for all students." %}
<div class="data-export-field-container">
<div class="data-export-field">
<label>
<span>{% trans "Problem types:" %}</span>
<select name="block_types">
<option value="all">All</option>
{% for label, value in block_choices.items %}
<option value="{{value}}">{{label}}</option>
{% endfor %}
</select>
</label>
</div>
</div>
</div>
<div class="data-export-field-container">
<div class="data-export-field">
<label>
<span>{% trans "Match answers containing:" %}</span>
<input type="text" name="match_string" />
</label>
</div>
<div class="data-export-helptext">
{% trans "Input text that all answers must match (case will be ignored). Otherwise, answers will not be filtered by content." %}
<div class="data-export-actions">
<button class="data-export-start">Search</button>
</div>
</div>
</div>
<div class="data-export-actions">
<button class="data-export-start">{% trans "Start a new export" %}</button>
</div>
<div class="data-export-results">
<h3>{% trans "Results" %}</h3>
<table>
<thead>
<tr>
......@@ -73,12 +66,13 @@
</thead>
<tbody></tbody>
</table>
<div class="data-export-info"></div>
</div>
<div class="data-export-status"></div>
<div class="data-export-actions">
<button class="data-export-download">{% trans "Download result" %}</button>
<button class="data-export-cancel">{% trans "Cancel current export" %}</button>
<button class="data-export-delete">{% trans "Delete result" %}</button>
<button class="data-export-download">{% trans "Download as CSV" %}</button>
<button class="data-export-cancel">{% trans "Cancel search" %}</button>
<button class="data-export-delete">{% trans "Delete results" %}</button>
</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