Skip to content

Commit

Permalink
map() returns a map object, not a list
Browse files Browse the repository at this point in the history
* Use unicodecsv module to write utf-8 to a csv file.
* Use BytesIO instead of StringIO
* Use Flask's `send_file` instead of `make_response`
  • Loading branch information
mwielgoszewski committed Jul 25, 2016
1 parent 154568a commit 3ea1f0c
Show file tree
Hide file tree
Showing 5 changed files with 21 additions and 20 deletions.
6 changes: 0 additions & 6 deletions doorman/compat.py
Expand Up @@ -18,12 +18,6 @@
basestring = (str, bytes)


try:
from StringIO import StringIO
except ImportError:
from io import StringIO


def with_metaclass(meta, *bases):
"""Create a base class with a metaclass."""
class metaclass(meta):
Expand Down
26 changes: 16 additions & 10 deletions doorman/manage/views.py
@@ -1,12 +1,13 @@
# -*- coding: utf-8 -*-
from io import BytesIO
from operator import itemgetter
import csv
import json
import datetime as dt
import unicodecsv as csv

from flask import (
Blueprint, current_app, flash, jsonify, make_response, redirect,
render_template, request, url_for
Blueprint, current_app, flash, jsonify, redirect, render_template,
request, send_file, url_for
)
from flask_login import login_required
from flask_paginate import Pagination
Expand All @@ -26,7 +27,6 @@
UpdateRuleForm,
UpdateNodeForm,
)
from doorman.compat import StringIO
from doorman.database import db
from doorman.models import (
DistributedQuery, DistributedQueryTask, DistributedQueryResult,
Expand Down Expand Up @@ -112,10 +112,10 @@ def nodes_csv():
column_names = map(itemgetter(0), current_app.config['DOORMAN_CAPTURE_NODE_INFO'])
labels = map(itemgetter(1), current_app.config['DOORMAN_CAPTURE_NODE_INFO'])
headers.extend(labels)
headers = map(str.title, headers)
headers = list(map(str.title, headers))

sio = StringIO()
writer = csv.writer(sio)
bio = BytesIO()
writer = csv.writer(bio)
writer.writerow(headers)

for node in Node.query:
Expand All @@ -130,9 +130,15 @@ def nodes_csv():
row.extend([node.node_info.get(column, '') for column in column_names])
writer.writerow(row)

response = make_response(sio.getvalue())
response.headers["Content-Disposition"] = "attachment; filename=nodes.csv"
response.headers["Content-Type"] = "text/csv"
bio.seek(0)

response = send_file(
bio,
mimetype='text/csv',
as_attachment=True,
attachment_filename='nodes.csv'
)

return response


Expand Down
1 change: 1 addition & 0 deletions requirements/dev.txt
Expand Up @@ -53,6 +53,7 @@ scales==1.0.9
six==1.10.0
SQLAlchemy==1.0.12
waitress==0.9.0
unicodecsv==0.14.1
webassets==0.11.1
WebOb==1.6.0
WebTest==2.0.21
Expand Down
1 change: 1 addition & 0 deletions requirements/prod.txt
Expand Up @@ -41,6 +41,7 @@ requests==2.10.0
requests-oauthlib==0.6.1
scales==1.0.9
SQLAlchemy==1.0.12
unicodecsv==0.14.1
webassets==0.11.1
Werkzeug==0.11.8
WTForms==2.1
7 changes: 3 additions & 4 deletions tests/test_functional.py
Expand Up @@ -1542,8 +1542,7 @@ def test_node_info_not_updated_on_erroneous_data(self, node, testapp):

class TestCSVExport:
def test_node_csv_download(self, node, testapp):
from doorman.compat import StringIO
import csv
import unicodecsv as csv

node.enrolled_on = dt.datetime.utcnow()
node.last_checkin = dt.datetime.utcnow()
Expand All @@ -1553,10 +1552,10 @@ def test_node_csv_download(self, node, testapp):

resp = testapp.get(url_for('manage.nodes_csv'))

assert resp.headers['Content-Type'] == 'text/csv'
assert resp.headers['Content-Type'] == 'text/csv; charset=utf-8'
assert resp.headers['Content-Disposition'] == 'attachment; filename=nodes.csv'

reader = csv.DictReader(StringIO(resp.body))
reader = csv.DictReader(io.BytesIO(resp.body))
row = next(reader)

assert row['Display Name'] == node.display_name
Expand Down

0 comments on commit 3ea1f0c

Please sign in to comment.