Add "download row results as CSV" feature to master view
This commit is contained in:
parent
f338a03c97
commit
c95e2dbb06
|
@ -40,9 +40,6 @@
|
|||
|
||||
<%def name="context_menu_items()">
|
||||
${parent.context_menu_items()}
|
||||
% if master.rows_downloadable and request.has_perm('{}.csv'.format(permission_prefix)):
|
||||
<li>${h.link_to("Download row data as CSV", url('{}.csv'.format(route_prefix), uuid=batch.uuid))}</li>
|
||||
% endif
|
||||
% if master.cloneable and request.has_perm('{}.clone'.format(permission_prefix)):
|
||||
<li>${h.link_to("Clone as new batch", url('{}.clone'.format(route_prefix), uuid=batch.uuid))}</li>
|
||||
% endif
|
||||
|
|
|
@ -61,6 +61,9 @@
|
|||
% if master.cloneable and request.has_perm('{}.clone'.format(permission_prefix)):
|
||||
<li>${h.link_to("Clone this as new {}".format(model_title), url('{}.clone'.format(route_prefix), uuid=instance.uuid))}</li>
|
||||
% endif
|
||||
% if master.rows_downloadable_csv and request.has_perm('{}.row_results_csv'.format(permission_prefix)):
|
||||
<li>${h.link_to("Download row results as CSV", url('{}.row_results_csv'.format(route_prefix), uuid=instance.uuid))}</li>
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<ul id="context-menu">
|
||||
|
|
|
@ -37,7 +37,6 @@ from sqlalchemy import orm
|
|||
|
||||
from rattail.db import model, Session as RattailSession
|
||||
from rattail.threads import Thread
|
||||
from rattail.csvutil import UnicodeDictWriter
|
||||
from rattail.util import load_object, prettify
|
||||
|
||||
import formalchemy as fa
|
||||
|
@ -64,7 +63,7 @@ class BatchMasterView(MasterView):
|
|||
default_handler_spec = None
|
||||
has_rows = True
|
||||
rows_deletable = True
|
||||
rows_downloadable = True
|
||||
rows_downloadable_csv = True
|
||||
refreshable = True
|
||||
refresh_after_create = False
|
||||
edit_with_rows = False
|
||||
|
@ -905,47 +904,14 @@ class BatchMasterView(MasterView):
|
|||
def get_execute_results_success_url(self, result, **kwargs):
|
||||
return self.get_index_url()
|
||||
|
||||
def csv(self):
|
||||
"""
|
||||
Download batch data as CSV.
|
||||
"""
|
||||
batch = self.get_instance()
|
||||
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.model_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)
|
||||
def get_row_csv_fields(self):
|
||||
fields = super(BatchMasterView, self).get_row_csv_fields()
|
||||
fields = [field for field in fields
|
||||
if field != 'removed' and not field.endswith('uuid')]
|
||||
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
|
||||
def get_row_results_csv_filename(self, batch):
|
||||
return '{}.{}.csv'.format(self.get_route_prefix(), batch.id_str)
|
||||
|
||||
def clone(self):
|
||||
"""
|
||||
|
@ -1021,14 +987,6 @@ class BatchMasterView(MasterView):
|
|||
config.add_tailbone_permission(permission_prefix, '{}.execute_multiple'.format(permission_prefix),
|
||||
"Execute multiple {}".format(model_title_plural))
|
||||
|
||||
# download rows as CSV
|
||||
if cls.rows_downloadable:
|
||||
config.add_route('{}.csv'.format(route_prefix), '{}/{{uuid}}/csv'.format(url_prefix))
|
||||
config.add_view(cls, attr='csv', route_name='{}.csv'.format(route_prefix),
|
||||
permission='{}.csv'.format(permission_prefix))
|
||||
config.add_tailbone_permission(permission_prefix, '{}.csv'.format(permission_prefix),
|
||||
"Download {} rows as CSV".format(model_title))
|
||||
|
||||
|
||||
class FileBatchMasterView(BatchMasterView):
|
||||
"""
|
||||
|
|
|
@ -112,6 +112,7 @@ class MasterView(View):
|
|||
rows_deletable_speedbump = False
|
||||
rows_bulk_deletable = False
|
||||
rows_default_pagesize = 20
|
||||
rows_downloadable_csv = False
|
||||
|
||||
mobile_rows_creatable = False
|
||||
mobile_rows_filterable = False
|
||||
|
@ -1319,6 +1320,9 @@ class MasterView(View):
|
|||
# default previously came from cls.get_normalized_model_name() but this is hopefully better
|
||||
return cls.get_route_prefix()
|
||||
|
||||
def get_row_grid_key(self):
|
||||
return '{}.{}'.format(self.get_grid_key(), self.request.matchdict[self.get_model_key()])
|
||||
|
||||
def get_grid_actions(self):
|
||||
main, more = self.get_main_actions(), self.get_more_actions()
|
||||
if len(more) == 1:
|
||||
|
@ -1472,6 +1476,29 @@ class MasterView(View):
|
|||
response.content_disposition = b'attachment; filename={}.csv'.format(self.get_grid_key())
|
||||
return response
|
||||
|
||||
def row_results_csv(self):
|
||||
"""
|
||||
Download current row results data for an object, as CSV
|
||||
"""
|
||||
obj = self.get_instance()
|
||||
fields = self.get_row_csv_fields()
|
||||
data = six.StringIO()
|
||||
writer = UnicodeDictWriter(data, fields)
|
||||
writer.writeheader()
|
||||
for row in self.get_effective_row_data(sort=True):
|
||||
writer.writerow(self.get_row_csv_row(row, fields))
|
||||
response = self.request.response
|
||||
response.body = data.getvalue()
|
||||
data.close()
|
||||
response.content_length = len(response.body)
|
||||
response.content_type = b'text/csv'
|
||||
filename = self.get_row_results_csv_filename(obj)
|
||||
response.content_disposition = b'attachment; filename={}'.format(filename)
|
||||
return response
|
||||
|
||||
def get_row_results_csv_filename(self, instance):
|
||||
return '{}.csv'.format(self.get_row_grid_key())
|
||||
|
||||
def get_csv_fields(self):
|
||||
"""
|
||||
Return the list of fields to be written to CSV download.
|
||||
|
@ -1483,6 +1510,17 @@ class MasterView(View):
|
|||
fields.append(prop.key)
|
||||
return fields
|
||||
|
||||
def get_row_csv_fields(self):
|
||||
"""
|
||||
Return the list of row fields to be written to CSV download.
|
||||
"""
|
||||
fields = []
|
||||
mapper = orm.class_mapper(self.model_row_class)
|
||||
for prop in mapper.iterate_properties:
|
||||
if isinstance(prop, orm.ColumnProperty):
|
||||
fields.append(prop.key)
|
||||
return fields
|
||||
|
||||
def get_csv_row(self, obj, fields):
|
||||
"""
|
||||
Return a dict for use when writing the row's data to CSV download.
|
||||
|
@ -1493,6 +1531,16 @@ class MasterView(View):
|
|||
csvrow[field] = '' if value is None else six.text_type(value)
|
||||
return csvrow
|
||||
|
||||
def get_row_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 six.text_type(value)
|
||||
return csvrow
|
||||
|
||||
##############################
|
||||
# CRUD Stuff
|
||||
##############################
|
||||
|
@ -2026,6 +2074,14 @@ class MasterView(View):
|
|||
|
||||
### sub-rows stuff follows
|
||||
|
||||
# download row results as CSV
|
||||
if cls.has_rows and cls.rows_downloadable_csv:
|
||||
config.add_tailbone_permission(permission_prefix, '{}.row_results_csv'.format(permission_prefix),
|
||||
"Download {} results as CSV".format(row_model_title))
|
||||
config.add_route('{}.row_results_csv'.format(route_prefix), '{}/{{uuid}}/rows-csv'.format(url_prefix))
|
||||
config.add_view(cls, attr='row_results_csv', route_name='{}.row_results_csv'.format(route_prefix),
|
||||
permission='{}.row_results_csv'.format(permission_prefix))
|
||||
|
||||
# create row
|
||||
if cls.has_rows:
|
||||
if cls.rows_creatable or cls.mobile_rows_creatable:
|
||||
|
|
|
@ -126,7 +126,7 @@ class MasterView2(MasterView):
|
|||
if factory is None:
|
||||
factory = self.get_row_grid_factory()
|
||||
if key is None:
|
||||
key = '{}.{}'.format(self.get_grid_key(), self.request.matchdict[self.get_model_key()])
|
||||
key = self.get_row_grid_key()
|
||||
if data is None:
|
||||
data = self.get_row_data(instance)
|
||||
if columns is None:
|
||||
|
|
Loading…
Reference in a new issue