Commit a66e21b7 by Braden MacDonald

Remove data export block

parent 8411e16d
...@@ -314,22 +314,6 @@ questions. ...@@ -314,22 +314,6 @@ questions.
![Mentoring Table](https://raw.githubusercontent.com/edx-solutions/xblock-mentoring/1fb5e3ece6f34b6cf33c956a4fba1d7cbb7349a2/doc/img/mentoring-table.png) ![Mentoring Table](https://raw.githubusercontent.com/edx-solutions/xblock-mentoring/1fb5e3ece6f34b6cf33c956a4fba1d7cbb7349a2/doc/img/mentoring-table.png)
### Data export
The Data export XBlock allows the instructor team to export all the
student answers to questions from the entire course as a CSV file. To
use the Data Export XBlock, you must make sure that
`"mentoring-dataexport"` is present in the `advanced_modules` list
under `Settings -> Advanced` in the Studio.
The Data export XBlock renders a button that will download a CSV file
when clicked.
![Data Export XBlock](https://raw.githubusercontent.com/edx-solutions/xblock-mentoring/1fb5e3ece6f34b6cf33c956a4fba1d7cbb7349a2/doc/img/dataexport.png)
The Data export XBlock is intented to be used as a tool by admins in
the Studio, and is not meant to be published on a live course.
### Maximum Attempts ### Maximum Attempts
You can set the number of maximum attempts for the unit completion by You can set the number of maximum attempts for the unit completion by
......
from .dataexport import MentoringDataExportBlock
from .mentoring import MentoringBlock from .mentoring import MentoringBlock
# -*- coding: utf-8 -*-
#
# Copyright (C) 2014 Harvard
#
# Authors:
# Xavier Antoviaque <xavier@antoviaque.org>
#
# 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 <http://www.gnu.org/licenses/>.
#
# Imports ###########################################################
import logging
import unicodecsv
from itertools import groupby
from StringIO import StringIO
from webob import Response
from xblock.core import XBlock
from xblock.fields import String, Scope
from xblock.fragment import Fragment
from xblockutils.resources import ResourceLoader
from .components.answer import AnswerBlock, Answer
# Globals ###########################################################
log = logging.getLogger(__name__)
loader = ResourceLoader(__name__)
# Utils ###########################################################
def list2csv(row):
"""
Convert a list to a CSV string (single row)
"""
f = StringIO()
writer = unicodecsv.writer(f, encoding='utf-8')
writer.writerow(row)
result = f.getvalue()
f.close()
return result
# Classes ###########################################################
class MentoringDataExportBlock(XBlock):
"""
An XBlock allowing the instructor team to export all the student answers as a CSV file
"""
display_name = String(help="Display name of the component", default="Mentoring Data Export",
scope=Scope.settings)
def student_view(self, context):
"""
Main view of the data export block
"""
# Count how many 'Answer' blocks are in this course:
num_answer_blocks = sum(1 for i in self._get_answer_blocks())
html = loader.render_template('templates/html/dataexport.html', {
'download_url': self.runtime.handler_url(self, 'download_csv'),
'num_answer_blocks': num_answer_blocks,
})
return Fragment(html)
@XBlock.handler
def download_csv(self, request, suffix=''):
response = Response(content_type='text/csv')
response.app_iter = self.get_csv()
response.content_disposition = 'attachment; filename=course_data.csv'
return response
def get_csv(self):
course_id = getattr(self.runtime, "course_id", "all")
answers = Answer.objects.filter(course_id=course_id).order_by('student_id', 'name')
answers_names = answers.values_list('name', flat=True).distinct().order_by('name')
# Header line
yield list2csv([u'student_id'] + list(answers_names))
if answers_names:
for _, student_answers in groupby(answers, lambda x: x.student_id):
row = []
next_answer_idx = 0
for answer in student_answers:
if not row:
row = [answer.student_id]
while answer.name != answers_names[next_answer_idx]:
# Still add answer row to CSV when they don't exist in DB
row.append('')
next_answer_idx += 1
row.append(answer.student_input)
next_answer_idx += 1
if row:
yield list2csv(row)
def _get_answer_blocks(self):
"""
Generator.
Searches the tree of XBlocks that includes this data export block
(i.e. search the current course)
and returns all the AnswerBlock blocks that we can see.
"""
root_block = self
while root_block.parent:
root_block = root_block.get_parent()
block_ids_left = set([root_block.scope_ids.usage_id])
while block_ids_left:
block = self.runtime.get_block(block_ids_left.pop())
if isinstance(block, AnswerBlock):
yield block
elif block.has_children:
block_ids_left |= set(block.children)
<div class="mentoring-dataexport">
<h3>Answers data dump</h3>
<p><a style="font-weight: bold;" href="{{ download_url }}">Download CSV Export</a> for the {{ num_answer_blocks }} answer{{ num_answer_blocks|pluralize }} in this course.</p>
</div>
<vertical_demo>
<html_demo>
<p>Below are two mentoring blocks and a grade export block that
should be able to export their data as CSV.</p>
</html_demo>
<mentoring display_name="Mentoring Block 1" mode="standard">
<html_demo>
<p>Please answer the question below.</p>
</html_demo>
<answer name="goal" question="What is your goal?">
</answer>
</mentoring>
<mentoring display_name="Mentoring Block 2 (Assessment)" mode="assessment">
<html_demo>
<p>Please answer the question below.</p>
</html_demo>
<answer name="inspired" question="Who has inspired you the most?">
</answer>
<answer name="meaning" question="What is the meaning of life?">
</answer>
</mentoring>
<mentoring-dataexport display_name="Data Export Test" />
</vertical_demo>
# -*- 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 <http://www.gnu.org/licenses/>.
#
# Imports ###########################################################
import ddt
import urllib2
from .base_test import MentoringBaseTest
# Classes ###########################################################
@ddt.ddt
class AnswerBlockTest(MentoringBaseTest):
"""
Test mentoring's data export tool.
"""
default_css_selector = 'body'
def go_to_page_as_student(self, page_name, student_id):
"""
Navigate to the page `page_name`, as listed on the workbench home
but override the student_id used
"""
self.browser.get(self.live_server_url)
href = self.browser.find_element_by_link_text(page_name).get_attribute("href")
href += "?student={}".format(student_id)
self.browser.get(href)
block = self.browser.find_element_by_css_selector(self._default_css_selector)
return block
def click_submit_button(self, page, mentoring_block_index):
"""
Click on one of the submit buttons on the page
"""
mentoring_div = page.find_elements_by_css_selector('div.mentoring')[mentoring_block_index]
submit = mentoring_div.find_element_by_css_selector('.submit .input-main')
self.assertTrue(submit.is_enabled())
submit.click()
self.wait_until_disabled(submit)
@ddt.data(
# student submissions, expected CSV text
(
[
("student10", "Essay1", "Essay2", "Essay3"),
("student20", "I didn't answer the last two questions", None, None),
],
(
u"student_id,goal,inspired,meaning\r\n"
"student10,Essay1,Essay2,Essay3\r\n"
"student20,I didn't answer the last two questions,,\r\n"
)
),
)
@ddt.unpack
def test_data_export_edit(self, submissions, expected_csv):
"""
Have students submit answers, then run an export and validate the output
"""
for student_id, answer1, answer2, answer3 in submissions:
page = self.go_to_page_as_student('Data Export', student_id)
answer1_field = page.find_element_by_css_selector('div[data-name=goal] textarea')
self.assertEqual(answer1_field.text, '')
answer1_field.send_keys(answer1)
self.click_submit_button(page, 0)
if answer2:
answer2_field = page.find_element_by_css_selector('div[data-name=inspired] textarea')
self.assertEqual(answer2_field.text, '')
answer2_field.send_keys(answer2)
self.click_submit_button(page, 1)
mentoring_div = page.find_elements_by_css_selector('div.mentoring')[1]
next = mentoring_div.find_element_by_css_selector('.submit .input-next')
next.click()
self.wait_until_disabled(next)
if answer3:
answer3_field = page.find_element_by_css_selector('div[data-name=meaning] textarea')
self.assertEqual(answer3_field.text, '')
answer3_field.send_keys(answer3)
self.click_submit_button(page, 1)
export_div = page.find_element_by_css_selector('.mentoring-dataexport')
self.assertIn("for the 3 answers in this course", export_div.text)
# Now "click" on the export link:
download_url = self.browser.find_element_by_link_text('Download CSV Export').get_attribute("href")
response = urllib2.urlopen(download_url)
headers = response.info()
self.assertTrue(headers['Content-Type'].startswith('text/csv'))
csv_data = response.read()
self.assertEqual(csv_data, expected_csv)
<vertical_demo>
<html_demo>
<p>Below are two mentoring blocks and a grade export block that
should be able to export their data as CSV.</p>
</html_demo>
<mentoring display_name="Mentoring Block 1" mode="standard">
<html_demo>
<p>Please answer the question below.</p>
</html_demo>
<answer name="goal" question="What is your goal?" />
</mentoring>
<mentoring display_name="Mentoring Block 2 (Assessment)" mode="assessment">
<html_demo>
<p>Please answer the question below.</p>
</html_demo>
<answer name="inspired" question="Who has inspired you the most?"/>
<answer name="meaning" question="What is the meaning of life?" />
</mentoring>
<mentoring-dataexport display_name="Data Export Test" />
</vertical_demo>
...@@ -44,7 +44,6 @@ def package_data(pkg, root_list): ...@@ -44,7 +44,6 @@ def package_data(pkg, root_list):
BLOCKS = [ BLOCKS = [
'mentoring = mentoring:MentoringBlock', 'mentoring = mentoring:MentoringBlock',
'mentoring-dataexport = mentoring:MentoringDataExportBlock',
'mentoring-table = mentoring.components:MentoringTableBlock', 'mentoring-table = mentoring.components:MentoringTableBlock',
'mentoring-column = mentoring.components:MentoringTableColumn', 'mentoring-column = mentoring.components:MentoringTableColumn',
......
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