Commit 5c4135e1 by Xavier Antoviaque

Allow to download data dump as a CSV file

parent 4808d543
from .answer import AnswerBlock from .answer import AnswerBlock
from .dataviewer import MentoringDataViewerBlock from .dataexport import MentoringDataExportBlock
from .quizz import QuizzBlock, QuizzTipBlock from .quizz import QuizzBlock, QuizzTipBlock
from .mentoring import MentoringBlock from .mentoring import MentoringBlock
from .table import MentoringTableBlock, MentoringTableColumnBlock from .table import MentoringTableBlock, MentoringTableColumnBlock
...@@ -3,14 +3,13 @@ ...@@ -3,14 +3,13 @@
# Imports ########################################################### # Imports ###########################################################
import logging import logging
import json
from webob import Response from webob import Response
from xblock.core import XBlock from xblock.core import XBlock
from xblock.fragment import Fragment from xblock.fragment import Fragment
from .models import Answer from .models import Answer
from .utils import load_resource, render_template from .utils import load_resource, list2csv, render_template
# Globals ########################################################### # Globals ###########################################################
...@@ -20,23 +19,21 @@ log = logging.getLogger(__name__) ...@@ -20,23 +19,21 @@ log = logging.getLogger(__name__)
# Classes ########################################################### # Classes ###########################################################
class MentoringDataViewerBlock(XBlock): class MentoringDataExportBlock(XBlock):
""" """
An XBlock allowing the instructor team to read/export all the student answers An XBlock allowing the instructor team to export all the student answers as a CSV file
""" """
def student_view(self, context): def student_view(self, context):
html = render_template('templates/html/dataviewer.html', { html = render_template('templates/html/dataexport.html', {
'self': self, 'self': self,
}) })
fragment = Fragment(html) fragment = Fragment(html)
fragment.add_javascript(load_resource('static/js/vendor/jquery.handsontable.full.js')) fragment.add_javascript(load_resource('static/js/dataexport.js'))
fragment.add_javascript(load_resource('static/js/dataviewer.js')) fragment.add_css(load_resource('static/css/dataexport.css'))
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') fragment.initialize_js('MentoringDataExportBlock')
return fragment return fragment
...@@ -44,22 +41,26 @@ class MentoringDataViewerBlock(XBlock): ...@@ -44,22 +41,26 @@ class MentoringDataViewerBlock(XBlock):
return Fragment(u'Studio view body') return Fragment(u'Studio view body')
@XBlock.handler @XBlock.handler
def get_data(self, request, suffix=''): def download_csv(self, request, suffix=''):
response_json = json.dumps({'data': self.get_data_list()}) response = Response(content_type='text/csv')
return Response(response_json, content_type='application/json') response.app_iter = self.get_csv()
response.content_disposition = 'attachment; filename=course_data.csv'
return response
def get_data_list(self): def get_csv(self):
answers = Answer.objects.all().order_by('student_id', 'name') answers = Answer.objects.all().order_by('student_id', 'name')
answers_names = Answer.objects.values_list('name', flat=True).distinct().order_by('name') answers_names = Answer.objects.values_list('name', flat=True).distinct().order_by('name')
data = [['student_id'] + list(answers_names)] # Header line
yield list2csv([u'student_id'] + list(answers_names))
row = [] row = []
cur_student_id = None cur_student_id = None
cur_col = None cur_col = None
for answer in answers: for answer in answers:
if answer.student_id != cur_student_id: if answer.student_id != cur_student_id:
if row: if row:
data.append(row) yield list2csv(row)
row = [answer.student_id] row = [answer.student_id]
cur_student_id = answer.student_id cur_student_id = answer.student_id
cur_col = 0 cur_col = 0
...@@ -69,9 +70,7 @@ class MentoringDataViewerBlock(XBlock): ...@@ -69,9 +70,7 @@ class MentoringDataViewerBlock(XBlock):
row.append(answer.student_input) row.append(answer.student_input)
cur_col += 1 cur_col += 1
if row: if row:
data.append(row) yield list2csv(row)
return data
@staticmethod @staticmethod
def workbench_scenarios(): def workbench_scenarios():
...@@ -79,6 +78,6 @@ class MentoringDataViewerBlock(XBlock): ...@@ -79,6 +78,6 @@ class MentoringDataViewerBlock(XBlock):
Sample scenarios which will be displayed in the workbench Sample scenarios which will be displayed in the workbench
""" """
return [ return [
("Mentoring - Page 999, Intructors data viewer", ("Mentoring - Page 999, Intructors data export",
load_resource('templates/xml/999_dataviewer.xml')), load_resource('templates/xml/999_dataexport.xml')),
] ]
...@@ -6,7 +6,9 @@ ...@@ -6,7 +6,9 @@
import logging import logging
import os import os
import pkg_resources import pkg_resources
import unicodecsv
from cStringIO import StringIO
from django.template import Context, Template from django.template import Context, Template
from xblock.fragment import Fragment from xblock.fragment import Fragment
...@@ -34,6 +36,16 @@ def render_template(template_path, context={}): ...@@ -34,6 +36,16 @@ def render_template(template_path, context={}):
template = Template(template_str) template = Template(template_str)
return template.render(Context(context)) return template.render(Context(context))
def list2csv(row):
"""
Convert a list to a CSV string (single row)
"""
f = StringIO()
writer = unicodecsv.writer(f, encoding='utf-8')
writer.writerow(row)
f.seek(0)
return f.read()
# Classes ########################################################### # Classes ###########################################################
......
...@@ -2,7 +2,7 @@ from setuptools import setup ...@@ -2,7 +2,7 @@ from setuptools import setup
BLOCKS = [ BLOCKS = [
'mentoring = mentoring:MentoringBlock', 'mentoring = mentoring:MentoringBlock',
'mentoring-dataviewer = mentoring:MentoringDataViewerBlock', 'mentoring-dataexport = mentoring:MentoringDataExportBlock',
'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 { .mentoring-dataexport {
overflow: scroll;
margin: 10px; margin: 10px;
} }
function MentoringDataExportBlock(runtime, element) {
var downloadUrl = runtime.handlerUrl(element, 'download_csv');
$('button.download', element).click(function(ev) {
ev.preventDefault();
window.location = downloadUrl;
});
}
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');
}
This source diff could not be displayed because it is too large. You can view the blob instead.
<div class="mentoring-dataexport">
<h3>Answers data dump</h3>
<button class="download">Download CSV</button>
</div>
<div class="mentoring-dataviewer">
<div class="mentoring-dataviewer-table"></div>
</div>
<vertical>
<mentoring-dataexport></mentoring-dataexport>
</vertical>
<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