Add "download row results as CSV" feature to master view
This commit is contained in:
		
							parent
							
								
									f338a03c97
								
							
						
					
					
						commit
						c95e2dbb06
					
				
					 5 changed files with 67 additions and 53 deletions
				
			
		|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lance Edgar
						Lance Edgar