Commit 07e4b885 by Tyler Hallada Committed by GitHub

AN-8229 Course List CSV download button (#642)

* Add download csv button to course list page

Also, a11y fix: make visible results-num span aria-hidden so the text is not
read twice by a screen-reader.

* Add acceptance test for downloading csv

* Use non-deprecated karma gulp runner

* Update jasmine-core npm package

* Output node version before running tests

* Try updating all karma-related npm packages

* Update translations
parent 5795af25
......@@ -19,6 +19,10 @@ export PATH=$PATH:$PWD/node_modules/.bin
# https://github.com/GeoNode/geonode/pull/1070
echo '{ "allow_root": true }' > /root/.bowerrc
# Output node.js version
node --version
npm --version
make develop
make migrate
......
import requests
from bok_choy.promise import EmptyPromise
from bok_choy.web_app_test import WebAppTest
from selenium.webdriver.common.keys import Keys
......@@ -30,6 +31,7 @@ class CourseIndexTests(AnalyticsDashboardWebAppTestMixin, WebAppTest):
self._test_clear_all_filters()
if ENABLE_COURSE_LIST_FILTERS:
self._test_filters()
self._test_download_csv()
def _test_course_list(self):
"""
......@@ -274,3 +276,22 @@ class CourseIndexTests(AnalyticsDashboardWebAppTestMixin, WebAppTest):
('instructor_paced', 'Instructor-Paced', False),
('self_paced', 'Self-Paced', True),
])
def _test_download_csv(self):
# Download button is present
download_button = self.page.q(css='a.action-download-data')
self.assertTrue(download_button.present)
link = download_button.attrs('href')[0]
# Steal the cookies from the logged-in firefox browser and use them in a python-initiated request
kwargs = dict()
session_id = [{i['name']: i['value']} for i in self.browser.get_cookies() if i['name'] == u'sessionid']
if session_id:
kwargs.update({
'cookies': session_id[0]
})
response = requests.get(link, **kwargs)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.headers['content-type'], 'text/csv')
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-07 15:07-0500\n"
"POT-Creation-Date: 2017-03-07 16:06-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -804,7 +804,7 @@ msgstr ""
msgid "External Tools"
msgstr ""
#: courses/templates/courses/index.html:9 courses/views/course_summaries.py:29
#: courses/templates/courses/index.html:9 courses/views/course_summaries.py:30
msgid "Courses"
msgstr ""
......@@ -1097,7 +1097,7 @@ msgid "Courseware"
msgstr ""
#. Translators: Do not translate UTC.
#: courses/views/course_summaries.py:38
#: courses/views/course_summaries.py:39
#, python-format
msgid ""
"Course summary data was last updated %(update_date)s at %(update_time)s UTC."
......
......@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-07 15:08-0500\n"
"POT-Creation-Date: 2017-03-07 16:06-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -376,7 +376,11 @@ msgstr ""
msgid "pacing_type"
msgstr ""
#: static/apps/course-list/list/views/course-list.js:79
#: static/apps/course-list/list/views/course-list.js:66
msgid "Download full course list to CSV"
msgstr ""
#: static/apps/course-list/list/views/course-list.js:91
#: static/dist/apps/course-list/app/course-list-main.js:6464
msgid "Course list controls"
msgstr ""
......
......@@ -4,6 +4,7 @@ from braces.views import LoginRequiredMixin
from django.core.exceptions import PermissionDenied
from django.http import Http404
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from waffle import switch_is_active
......@@ -53,7 +54,8 @@ class CourseIndex(CourseAPIMixin, LoginRequiredMixin, TrackedViewMixin, LastUpda
data = {
'course_list_json': summaries,
'enable_course_filters': switch_is_active('enable_course_filters')
'enable_course_filters': switch_is_active('enable_course_filters'),
'course_list_download_url': reverse('courses:index_csv'),
}
context['js_data']['course'] = data
context['page_data'] = self.get_page_data(context)
......
<span class="num-results"><%- numResults %></span>
<span class="num-results" aria-hidden="true"><%- numResults %></span>
......@@ -5,10 +5,10 @@
</div>
<div class="col col-12 sm-col-12 md-col-10 course-list-results-col">
<div class="row">
<div class="col col-12 sm-col-12 md-col-3 md-pre-9">
<div class="col col-12 sm-col-12 md-col-3">
<div class="course-list-num-results"></div>
</div>
<div class="col col-12 sm-col-12 md-col-3">
<div class="col col-12 sm-col-12 md-col-3 md-pre-6">
<div class="course-list-download-data"></div>
</div>
</div>
......
......@@ -12,6 +12,7 @@ define(function(require) {
ActiveFiltersView = require('components/generic-list/list/views/active-filters'),
CourseListControlsView = require('course-list/list/views/controls'),
CourseListResultsView = require('course-list/list/views/results'),
DownloadDataView = require('components/download/views/download-data'),
ListView = require('components/generic-list/list/views/list'),
NumResultsView = require('components/generic-list/list/views/num-results'),
......@@ -27,6 +28,7 @@ define(function(require) {
regions: {
activeFilters: '.course-list-active-filters',
controls: '.course-list-table-controls',
downloadData: '.course-list-download-data',
results: '.course-list-results',
numResults: '.course-list-num-results'
},
......@@ -55,6 +57,16 @@ define(function(require) {
}
},
{
region: 'downloadData',
class: DownloadDataView,
options: {
collection: this.options.collection,
trackingModel: this.options.trackingModel,
trackCategory: 'course_list',
downloadDataMessage: gettext('Download full course list to CSV')
}
},
{
region: 'results',
class: CourseListResultsView,
options: {
......
......@@ -798,21 +798,6 @@ body.view-dashboard {
}
.learners-results-col {
@media (min-width: $bp-screen-md) {
.row .col:first-child {
margin-top: $padding-large-vertical * 1.6;
}
}
.learners-num-results {
text-align: left;
margin-bottom: 0px;
margin-left: $padding-xs-horizontal;
margin-right: 0px;
}
}
}
// styles for the learner details summary
......
......@@ -56,11 +56,18 @@
}
}
.#{$app-name}-num-results {
text-align: right;
margin-bottom: $padding-small-vertical;
margin-right: $padding-xs-horizontal;
font-size: $font-size-small;
.#{$app-name}-results-col {
@media (min-width: $bp-screen-md) {
.row .col:first-child {
margin-top: $padding-large-vertical * 1.6;
}
}
.#{$app-name}-num-results {
text-align: left;
margin-left: $padding-xs-horizontal;
font-size: $font-size-small;
}
}
.section-action {
......
......@@ -3,7 +3,7 @@
var eslint = require('gulp-eslint'),
gulp = require('gulp'),
karma = require('karma').server,
Server = require('karma').Server,
path = require('path'),
browserSync = require('browser-sync'),
extend = require('util')._extend, // eslint-disable-line no-underscore-dangle
......@@ -36,7 +36,7 @@
singleRun: true,
browsers: ['PhantomJS']
};
karma.start(extend(defaultOptions, options), cb);
new Server(extend(defaultOptions, options), cb).start();
}
gulp.task('lint', function() {
......
......@@ -16,20 +16,19 @@
"eslint-config-edx": "^1.2.0",
"gulp": "^3.8.8",
"gulp-eslint": "^2.0.0",
"gulp-karma": "0.0.5",
"jasmine-core": "^2.4.1",
"jasmine-core": "^2.5.2",
"jscs": "^1.10.0",
"karma": "^1.3.0",
"karma-coverage": "^0.2.6",
"karma-chrome-launcher": "^0.2.3",
"karma-jasmine": "^0.3.6",
"karma": "^1.5.0",
"karma-chrome-launcher": "^2.0.0",
"karma-coverage": "^1.1.1",
"karma-jasmine": "^1.1.0",
"karma-jasmine-html-reporter": "^0.2.2",
"karma-jasmine-jquery": "^0.1.1",
"karma-jasmine-html-reporter": "^0.2.0",
"karma-phantomjs-launcher": "^1.0.0",
"karma-requirejs": "^0.2.2",
"karma-sinon": "^1.0.3",
"phantomjs-prebuilt": "^2.1.7",
"sinon": "1.17.3"
"karma-phantomjs-launcher": "^1.0.2",
"karma-requirejs": "^1.1.0",
"karma-sinon": "^1.0.5",
"phantomjs-prebuilt": "^2.1.14",
"sinon": "1.17.7"
},
"eslintConfig": {
"extends": "eslint-config-edx"
......
# Test dependencies go here.
-r base.txt
bok-choy>=0.4.7
bok-choy>=0.6.2
coverage==4.2
ddt==1.1.0
django-dynamic-fixture==1.9.0
......
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