Add ability to download batch row data as CSV.

This commit is contained in:
Lance Edgar 2015-08-19 20:06:13 -05:00
parent 6c5eec7981
commit 73939b825e
3 changed files with 78 additions and 12 deletions

View file

@ -42,21 +42,25 @@
</style> </style>
</%def> </%def>
<%def name="context_menu_items()">
<li>${h.link_to("Back to {0}".format(batch_display_plural), url(route_prefix))}</li>
% if not batch.executed:
% if form.updating:
<li>${h.link_to("View this {0}".format(batch_display), url('{0}.view'.format(route_prefix), uuid=batch.uuid))}</li>
% endif
% if form.readonly and request.has_perm('{0}.edit'.format(permission_prefix)):
<li>${h.link_to("Edit this {0}".format(batch_display), url('{0}.edit'.format(route_prefix), uuid=batch.uuid))}</li>
% endif
% endif
% if request.has_perm('{0}.delete'.format(permission_prefix)):
<li>${h.link_to("Delete this {0}".format(batch_display), url('{0}.delete'.format(route_prefix), uuid=batch.uuid))}</li>
% endif
</%def>
<div class="form-wrapper"> <div class="form-wrapper">
<ul class="context-menu"> <ul class="context-menu">
<li>${h.link_to("Back to {0}".format(batch_display_plural), url(route_prefix))}</li> ${self.context_menu_items()}
% if not batch.executed:
% if form.updating:
<li>${h.link_to("View this {0}".format(batch_display), url('{0}.view'.format(route_prefix), uuid=batch.uuid))}</li>
% endif
% if form.readonly and request.has_perm('{0}.edit'.format(permission_prefix)):
<li>${h.link_to("Edit this {0}".format(batch_display), url('{0}.edit'.format(route_prefix), uuid=batch.uuid))}</li>
% endif
% endif
% if request.has_perm('{0}.delete'.format(permission_prefix)):
<li>${h.link_to("Delete this {0}".format(batch_display), url('{0}.delete'.format(route_prefix), uuid=batch.uuid))}</li>
% endif
</ul> </ul>
${form.render(form_id='batch-form', buttons=capture(buttons))|n} ${form.render(form_id='batch-form', buttons=capture(buttons))|n}

View file

@ -13,6 +13,13 @@
</script> </script>
</%def> </%def>
<%def name="context_menu_items()">
${parent.context_menu_items()}
% if request.has_perm('{0}.csv'.format(permission_prefix)):
<li>${h.link_to("Download this {0} as CSV".format(batch_display), url('{0}.csv'.format(route_prefix), uuid=batch.uuid))}</li>
% endif
</%def>
<%def name="buttons()"> <%def name="buttons()">
<div class="buttons"> <div class="buttons">
% if not form.readonly and batch.refreshable: % if not form.readonly and batch.refreshable:

View file

@ -32,6 +32,9 @@ from __future__ import unicode_literals
import os import os
import datetime import datetime
import logging import logging
from cStringIO import StringIO
from sqlalchemy import orm
import formalchemy import formalchemy
from pyramid.renderers import render_to_response from pyramid.renderers import render_to_response
@ -42,6 +45,7 @@ from webhelpers.html.tags import link_to, HTML
from rattail.db import model from rattail.db import model
from rattail.db import Session as RatSession from rattail.db import Session as RatSession
from rattail.threads import Thread from rattail.threads import Thread
from rattail.csvutil import UnicodeDictWriter
from tailbone.db import Session from tailbone.db import Session
from tailbone.views import SearchableAlchemyGridView, CrudView from tailbone.views import SearchableAlchemyGridView, CrudView
@ -366,6 +370,10 @@ class BatchCrud(BaseCrud):
def batch_class(self): def batch_class(self):
raise NotImplementedError raise NotImplementedError
@property
def batch_row_class(self):
raise NotImplementedError
@property @property
def mapped_class(self): def mapped_class(self):
return self.batch_class return self.batch_class
@ -705,6 +713,48 @@ class BatchCrud(BaseCrud):
progress.session['success_url'] = self.view_url(batch.uuid) progress.session['success_url'] = self.view_url(batch.uuid)
progress.session.save() progress.session.save()
def csv(self):
"""
Download batch data as CSV.
"""
batch = self.current_batch()
fields = self.get_csv_fields()
data = StringIO()
writer = UnicodeDictWriter(data, fields)
writer.writeheader()
for row in batch.data_rows:
if not row.removed:
writer.writerow(self.get_csv_row(row, fields))
response = self.request.response
response.text = data.getvalue().decode('utf_8')
data.close()
response.content_length = len(response.text)
response.content_type = b'text/csv'
response.content_disposition = b'attachment; filename=batch.csv'
return response
def get_csv_fields(self):
"""
Return the list of fields to be written to CSV download.
"""
fields = []
mapper = orm.class_mapper(self.batch_row_class)
for prop in mapper.iterate_properties:
if isinstance(prop, orm.ColumnProperty):
if prop.key != 'removed' and not prop.key.endswith('uuid'):
fields.append(prop.key)
return fields
def get_csv_row(self, row, fields):
"""
Return a dict for use when writing the row's data to CSV download.
"""
csvrow = {}
for field in fields:
value = getattr(row, field)
csvrow[field] = '' if value is None else unicode(value)
return csvrow
class FileBatchCrud(BatchCrud): class FileBatchCrud(BatchCrud):
""" """
@ -1163,6 +1213,11 @@ def defaults(config, batch_grid, batch_crud, row_grid, row_crud, url_prefix,
config.add_view(batch_crud, attr='execute', route_name='{0}.execute'.format(route_prefix), config.add_view(batch_crud, attr='execute', route_name='{0}.execute'.format(route_prefix),
permission='{0}.execute'.format(permission_prefix)) permission='{0}.execute'.format(permission_prefix))
# Download batch row data as CSV
config.add_route('{0}.csv'.format(route_prefix), '{0}{{uuid}}/csv'.format(url_prefix))
config.add_view(batch_crud, attr='csv', route_name='{0}.csv'.format(route_prefix),
permission='{0}.csv'.format(permission_prefix))
# Download batch data file # Download batch data file
if hasattr(batch_crud, 'download'): if hasattr(batch_crud, 'download'):
config.add_route('{0}.download'.format(route_prefix), '{0}{{uuid}}/download'.format(url_prefix)) config.add_route('{0}.download'.format(route_prefix), '{0}{{uuid}}/download'.format(url_prefix))