Commit 4808d543 by Xavier Antoviaque

Compile a full dump of the students answers

parent 32ca54f3
from .answer import AnswerBlock from .answer import AnswerBlock
from .dataviewer import MentoringDataViewerBlock
from .quizz import QuizzBlock, QuizzTipBlock from .quizz import QuizzBlock, QuizzTipBlock
from .table import MentoringTableBlock, MentoringTableColumnBlock
from .mentoring import MentoringBlock from .mentoring import MentoringBlock
from .table import MentoringTableBlock, MentoringTableColumnBlock
...@@ -101,7 +101,7 @@ class AnswerBlock(XBlock): ...@@ -101,7 +101,7 @@ class AnswerBlock(XBlock):
if not name: if not name:
raise ValueError, 'AnswerBlock.name field need to be set to a non-null/empty value' raise ValueError, 'AnswerBlock.name field need to be set to a non-null/empty value'
# TODO Use a random user id # TODO Use anonymous_user_id
student_id = self.scope_ids.user_id student_id = self.scope_ids.user_id
answer_data, created = Answer.objects.get_or_create( answer_data, created = Answer.objects.get_or_create(
......
# -*- coding: utf-8 -*-
# Imports ###########################################################
import logging
import json
from webob import Response
from xblock.core import XBlock
from xblock.fragment import Fragment
from .models import Answer
from .utils import load_resource, render_template
# Globals ###########################################################
log = logging.getLogger(__name__)
# Classes ###########################################################
class MentoringDataViewerBlock(XBlock):
"""
An XBlock allowing the instructor team to read/export all the student answers
"""
def student_view(self, context):
html = render_template('templates/html/dataviewer.html', {
'self': self,
})
fragment = Fragment(html)
fragment.add_javascript(load_resource('static/js/vendor/jquery.handsontable.full.js'))
fragment.add_javascript(load_resource('static/js/dataviewer.js'))
fragment.add_css(load_resource('static/css/vendor/jquery.handsontable.full.css'))
fragment.add_css(load_resource('static/css/dataviewer.css'))
fragment.initialize_js('MentoringDataViewerBlock')
return fragment
def studio_view(self, context):
return Fragment(u'Studio view body')
@XBlock.handler
def get_data(self, request, suffix=''):
response_json = json.dumps({'data': self.get_data_list()})
return Response(response_json, content_type='application/json')
def get_data_list(self):
answers = Answer.objects.all().order_by('student_id', 'name')
answers_names = Answer.objects.values_list('name', flat=True).distinct().order_by('name')
data = [['student_id'] + list(answers_names)]
row = []
cur_student_id = None
cur_col = None
for answer in answers:
if answer.student_id != cur_student_id:
if row:
data.append(row)
row = [answer.student_id]
cur_student_id = answer.student_id
cur_col = 0
while answer.name != answers_names[cur_col]:
row.append('')
cur_col += 1
row.append(answer.student_input)
cur_col += 1
if row:
data.append(row)
return data
@staticmethod
def workbench_scenarios():
"""
Sample scenarios which will be displayed in the workbench
"""
return [
("Mentoring - Page 999, Intructors data viewer",
load_resource('templates/xml/999_dataviewer.xml')),
]
...@@ -2,6 +2,7 @@ from setuptools import setup ...@@ -2,6 +2,7 @@ from setuptools import setup
BLOCKS = [ BLOCKS = [
'mentoring = mentoring:MentoringBlock', 'mentoring = mentoring:MentoringBlock',
'mentoring-dataviewer = mentoring:MentoringDataViewerBlock',
'mentoring-table = mentoring:MentoringTableBlock', 'mentoring-table = mentoring:MentoringTableBlock',
'column = mentoring:MentoringTableColumnBlock', 'column = mentoring:MentoringTableColumnBlock',
'answer = mentoring:AnswerBlock', 'answer = mentoring:AnswerBlock',
......
.mentoring-dataviewer .mentoring-dataviewer-table {
overflow: scroll;
margin: 10px;
}
/**
* Handsontable 0.9.19
* Handsontable is a simple jQuery plugin for editable tables with basic copy-paste compatibility with Excel and Google Docs
*
* Copyright 2012, Marcin Warpechowski
* Licensed under the MIT license.
* http://handsontable.com/
*
* Date: Tue Oct 01 2013 13:17:18 GMT+0200 (Central European Daylight Time)
*/
.handsontable {
position: relative;
font-family: Arial, Helvetica, sans-serif;
line-height: 1.3em;
font-size: 13px;
}
.handsontable.htAutoColumnSize {
visibility: hidden;
left: 0;
position: absolute;
top: 0;
}
.handsontable table,
.handsontable tbody,
.handsontable thead,
.handsontable td,
.handsontable th,
.handsontable div
{
box-sizing: content-box;
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
}
.handsontable table.htCore {
border-collapse: separate; /*it must be separate, otherwise there are offset miscalculations in WebKit: http://stackoverflow.com/questions/2655987/border-collapse-differences-in-ff-and-webkit*/
position: relative;
/*this actually only changes appearance of user selection - does not make text unselectable
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-o-user-select: none;
-ms-user-select: none;
/*user-select: none; /*no browser supports unprefixed version*/
border-spacing: 0;
margin: 0;
border-width: 0;
table-layout: fixed;
width: 0;
outline-width: 0;
/* reset bootstrap table style. for more info see: https://github.com/warpech/jquery-handsontable/issues/224 */
max-width: none;
max-height: none;
}
.handsontable col {
width: 50px;
}
.handsontable col.rowHeader {
width: 50px;
}
.handsontable th,
.handsontable td {
border-right: 1px solid #CCC;
border-bottom: 1px solid #CCC;
height: 22px;
empty-cells: show;
line-height: 21px;
padding: 0 4px 0 4px; /* top, bottom padding different than 0 is handled poorly by FF with HTML5 doctype */
background-color: #FFF;
font-size: 12px;
vertical-align: top;
overflow: hidden;
outline-width: 0;
white-space: pre-line; /* preserve new line character in cell */
}
.handsontable td.htInvalid {
-webkit-transition: background 0.75s ease;
transition: background 0.75s ease;
background-color: #ff4c42;
}
.handsontable th:last-child {
/*Foundation framework fix*/
border-right: 1px solid #CCC;
border-bottom: 1px solid #CCC;
}
.handsontable tr:first-child th.htNoFrame,
.handsontable th:first-child.htNoFrame,
.handsontable th.htNoFrame {
border-left-width: 0;
background-color: white;
border-color: #FFF;
}
.handsontable th:first-child,
.handsontable td:first-child,
.handsontable .htNoFrame + th,
.handsontable .htNoFrame + td {
border-left: 1px solid #CCC;
}
.handsontable tr:first-child th,
.handsontable tr:first-child td {
border-top: 1px solid #CCC;
}
.handsontable thead tr:last-child th {
border-bottom-width: 0;
}
.handsontable thead tr.lastChild th {
border-bottom-width: 0;
}
.handsontable th {
background-color: #EEE;
color: #222;
text-align: center;
font-weight: normal;
white-space: nowrap;
}
.handsontable th .small {
font-size: 12px;
}
.handsontable thead th {
padding: 0;
}
.handsontable th.active {
background-color: #CCC;
}
.handsontable thead th .relative {
position: relative;
padding: 2px 4px;
}
/* plugins */
.handsontable .manualColumnMover {
position: absolute;
left: 0;
top: 0;
background-color: transparent;
width: 5px;
height: 25px;
z-index: 999;
cursor: move;
}
.handsontable th .manualColumnMover:hover,
.handsontable th .manualColumnMover.active {
background-color: #88F;
}
.handsontable .manualColumnResizer {
position: absolute;
top: 0;
cursor: col-resize;
}
.handsontable .manualColumnResizerHandle {
background-color: transparent;
width: 5px;
height: 25px;
}
.handsontable .manualColumnResizer:hover .manualColumnResizerHandle,
.handsontable .manualColumnResizer.active .manualColumnResizerHandle {
background-color: #AAB;
}
.handsontable .manualColumnResizerLine {
position: absolute;
right: 0;
top: 0;
background-color: #AAB;
display: none;
width: 0;
border-right: 1px dashed #777;
}
.handsontable .manualColumnResizer.active .manualColumnResizerLine {
display: block;
}
.handsontable .columnSorting:hover {
text-decoration: underline;
cursor: pointer;
}
/* border line */
.handsontable .wtBorder {
position: absolute;
font-size: 0;
}
.handsontable td.area {
background-color: #EEF4FF;
}
/* fill handle */
.handsontable .wtBorder.corner {
font-size: 0;
cursor: crosshair;
}
.handsontable .htBorder.htFillBorder {
background: red;
width: 1px;
height: 1px;
}
.handsontableInput {
border: 2px solid #5292F7;
outline-width: 0;
margin: 0;
padding: 1px 4px 0 2px;
font-family: Arial, Helvetica, sans-serif; /*repeat from .handsontable (inherit doesn't work with IE<8) */
line-height: 1.3em; /*repeat from .handsontable (inherit doesn't work with IE<8) */
font-size: 13px;
-webkit-box-shadow: 1px 2px 5px rgba(0, 0, 0, 0.4);
box-shadow: 1px 2px 5px rgba(0, 0, 0, 0.4);
resize: none;
/*below are needed to overwrite stuff added by jQuery UI Bootstrap theme*/
display: inline-block;
font-size: 13px;
color: #000;
border-radius: 0;
}
.handsontableInputHolder {
position: absolute;
top: 0;
left: 0;
width: 1px;
height: 1px;
}
/*
TextRenderer readOnly cell
*/
.handsontable .htDimmed {
font-style: italic;
color: #777;
}
/*
AutocompleteRenderer down arrow
*/
.handsontable .htAutocomplete {
position: relative;
padding-right: 20px;
}
.handsontable .htAutocompleteArrow {
position: absolute;
top: 0;
right: 0;
font-size: 10px;
color: #EEE;
cursor: default;
width: 16px;
text-align: center;
}
.handsontable td .htAutocompleteArrow:hover {
color: #777;
}
/*
CheckboxRenderer
*/
.handsontable .htCheckboxRendererInput.noValue {
opacity: 0.5;
}
/*
NumericRenderer
*/
.handsontable .htNumeric {
text-align: right;
}
/* typeahead rules. Needed only if you are using the autocomplete feature */
.handsontable .typeahead {
position: absolute;
font-family: Arial, Helvetica, sans-serif;
line-height: 1.3em;
font-size: 13px;
z-index: 10;
top: 100%;
left: 0;
float: left;
display: none;
min-width: 160px;
padding: 4px 0;
margin: 2px 0 0 0;
list-style: none;
background-color: white;
border-color: #CCC;
border-color: rgba(0, 0, 0, 0.2);
border-style: solid;
border-width: 1px;
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
-webkit-background-clip: padding-box;
background-clip: padding-box;
border-radius: 4px;
}
.handsontable .typeahead li {
line-height: 18px;
min-height: 18px;
display: list-item;
margin: 0;
}
.handsontable .typeahead a {
display: block;
padding: 3px 15px;
clear: both;
font-weight: normal;
line-height: 18px;
min-height: 18px;
color: #333;
white-space: nowrap;
}
.handsontable .typeahead li > a:hover,
.handsontable .typeahead .active > a,
.handsontable .typeahead .active > a:hover {
color: white;
text-decoration: none;
background-color: #08C;
}
.handsontable .typeahead a {
color: #08C;
text-decoration: none;
}
/*context menu rules*/
ul.context-menu-list {
color: black;
}
ul.context-menu-list li {
margin-bottom: 0; /*Foundation framework fix*/
}
/**
* dragdealer
*/
.handsontable .dragdealer {
position: relative;
width: 9px;
height: 9px;
background: #F8F8F8;
border: 1px solid #DDD;
}
.handsontable .dragdealer .handle {
position: absolute;
width: 9px;
height: 9px;
background: #C5C5C5;
}
.handsontable .dragdealer .disabled {
background: #898989;
}
/*!
* jQuery contextMenu - Plugin for simple contextMenu handling
*
* Version: 1.6.5
*
* Authors: Rodney Rehm, Addy Osmani (patches for FF)
* Web: http://medialize.github.com/jQuery-contextMenu/
*
* Licensed under
* MIT License http://www.opensource.org/licenses/mit-license
* GPL v3 http://opensource.org/licenses/GPL-3.0
*
*/
.context-menu-list {
margin:0;
padding:0;
min-width: 120px;
max-width: 250px;
display: inline-block;
position: absolute;
list-style-type: none;
border: 1px solid #DDD;
background: #EEE;
-webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
-moz-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
-ms-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
-o-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
font-family: Verdana, Arial, Helvetica, sans-serif;
font-size: 11px;
}
.context-menu-item {
padding: 2px 2px 2px 24px;
background-color: #EEE;
position: relative;
-webkit-user-select: none;
-moz-user-select: -moz-none;
-ms-user-select: none;
user-select: none;
}
.context-menu-separator {
padding-bottom:0;
border-bottom: 1px solid #DDD;
}
.context-menu-item > label > input,
.context-menu-item > label > textarea {
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
}
.context-menu-item.hover {
cursor: pointer;
background-color: #39F;
}
.context-menu-item.disabled {
color: #666;
}
.context-menu-input.hover,
.context-menu-item.disabled.hover {
cursor: default;
background-color: #EEE;
}
.context-menu-submenu:after {
content: ">";
color: #666;
position: absolute;
top: 0;
right: 3px;
z-index: 1;
}
/* icons
#protip:
In case you want to use sprites for icons (which I would suggest you do) have a look at
http://css-tricks.com/13224-pseudo-spriting/ to get an idea of how to implement
.context-menu-item.icon:before {}
*/
.context-menu-item.icon { min-height: 18px; background-repeat: no-repeat; background-position: 4px 2px; }
.context-menu-item.icon-edit { background-image: url(images/page_white_edit.png); }
.context-menu-item.icon-cut { background-image: url(images/cut.png); }
.context-menu-item.icon-copy { background-image: url(images/page_white_copy.png); }
.context-menu-item.icon-paste { background-image: url(images/page_white_paste.png); }
.context-menu-item.icon-delete { background-image: url(images/page_white_delete.png); }
.context-menu-item.icon-add { background-image: url(images/page_white_add.png); }
.context-menu-item.icon-quit { background-image: url(images/door.png); }
/* vertically align inside labels */
.context-menu-input > label > * { vertical-align: top; }
/* position checkboxes and radios as icons */
.context-menu-input > label > input[type="checkbox"],
.context-menu-input > label > input[type="radio"] {
margin-left: -17px;
}
.context-menu-input > label > span {
margin-left: 5px;
}
.context-menu-input > label,
.context-menu-input > label > input[type="text"],
.context-menu-input > label > textarea,
.context-menu-input > label > select {
display: block;
width: 100%;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
-o-box-sizing: border-box;
box-sizing: border-box;
}
.context-menu-input > label > textarea {
height: 100px;
}
.context-menu-item > .context-menu-list {
display: none;
/* re-positioned by js */
right: -5px;
top: 5px;
}
.context-menu-item.hover > .context-menu-list {
display: block;
}
.context-menu-accesskey {
text-decoration: underline;
}
function MentoringDataViewerBlock(runtime, element) {
var handlerUrl = runtime.handlerUrl(element, 'get_data');
$.get(handlerUrl, function(result) {
$('.mentoring-dataviewer .mentoring-dataviewer-table', element).handsontable({
data: result.data,
colWidths: 300,
rowHeaders: true,
colHeaders: true,
stretchH: 'all'
});
}, 'json');
}
...@@ -39,7 +39,7 @@ function MentoringBlock(runtime, element) { ...@@ -39,7 +39,7 @@ function MentoringBlock(runtime, element) {
data[child.name] = callIfExists(child, 'submit'); data[child.name] = callIfExists(child, 'submit');
} }
} }
var handlerUrl = runtime.handlerUrl(element, 'submit') var handlerUrl = runtime.handlerUrl(element, 'submit');
$.post(handlerUrl, JSON.stringify(data)).success(handleSubmitResults); $.post(handlerUrl, JSON.stringify(data)).success(handleSubmitResults);
}); });
......
This source diff could not be displayed because it is too large. You can view the blob instead.
<div class="mentoring-dataviewer">
<div class="mentoring-dataviewer-table"></div>
</div>
<vertical>
<html>
<h3>Answers data</h3>
</html>
<mentoring-dataviewer></mentoring-dataviewer>
</vertical>
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