Wrap up initial vendor catalog batch support etc.

* Adds the ability to delete all batch rows matching current query.
* Refactors some progress factory args.
* If batch initialization fails, don't persist batch.
This commit is contained in:
Lance Edgar 2015-02-12 21:35:28 -06:00
parent c28a6b2e09
commit 16be06821a
4 changed files with 65 additions and 9 deletions

View file

@ -7,6 +7,11 @@
${search.render()} ${search.render()}
</td> </td>
</tr> </tr>
<tr>
<td class="tools">
<p>${h.link_to("Delete all rows matching current search", url('{0}.rows.bulk_delete'.format(route_prefix), uuid=batch.uuid))}</p>
</td>
</tr>
</table> </table>
${grid} ${grid}

View file

@ -473,11 +473,11 @@ class BatchCrud(BaseCrud):
} }
return render_to_response('/progress.mako', kwargs, request=self.request) return render_to_response('/progress.mako', kwargs, request=self.request)
def refresh_data(self, session, batch, progress_factory=None): def refresh_data(self, session, batch, progress=None):
""" """
Instruct the batch handler to refresh all data for the batch. Instruct the batch handler to refresh all data for the batch.
""" """
self.handler.refresh_data(session, batch, progress_factory=progress_factory) self.handler.refresh_data(session, batch, progress=progress)
batch.cognized = datetime.datetime.utcnow() batch.cognized = datetime.datetime.utcnow()
batch.cognized_by = self.request.user batch.cognized_by = self.request.user
@ -490,7 +490,7 @@ class BatchCrud(BaseCrud):
# transaction binding etc. # transaction binding etc.
session = RatSession() session = RatSession()
batch = session.query(self.batch_class).get(batch_uuid) batch = session.query(self.batch_class).get(batch_uuid)
self.refresh_data(session, batch, progress_factory=progress) self.refresh_data(session, batch, progress=progress)
session.commit() session.commit()
session.refresh(batch) session.refresh(batch)
session.close() session.close()
@ -591,7 +591,9 @@ class FileBatchCrud(BatchCrud):
def save_form(self, form): def save_form(self, form):
""" """
Save the uploaded data file if necessary, etc. Save the uploaded data file if necessary, etc. If batch initialization
fails, don't persist the batch at all; the user will be sent back to
the "create batch" page in that case.
""" """
# Transfer form data to batch instance. # Transfer form data to batch instance.
form.fieldset.sync() form.fieldset.sync()
@ -603,7 +605,8 @@ class FileBatchCrud(BatchCrud):
batch.filename = form.fieldset.data_file.renderer._filename batch.filename = form.fieldset.data_file.renderer._filename
# Expunge batch from session to prevent it from being flushed. # Expunge batch from session to prevent it from being flushed.
Session.expunge(batch) Session.expunge(batch)
self.init_batch(batch) self.batch_inited = self.init_batch(batch)
if self.batch_inited:
Session.add(batch) Session.add(batch)
batch.write_file(self.request.rattail_config, form.fieldset.data_file.value) batch.write_file(self.request.rattail_config, form.fieldset.data_file.value)
@ -613,7 +616,24 @@ class FileBatchCrud(BatchCrud):
effectively provide default values for a batch, etc. This method is effectively provide default values for a batch, etc. This method is
invoked after a batch has been fully prepared for insertion to the invoked after a batch has been fully prepared for insertion to the
database, but before the push to the database occurs. database, but before the push to the database occurs.
Note that the return value of this function matters; if it is boolean
false then the batch will not be persisted at all, and the user will be
redirected to the "create batch" page.
""" """
return True
def post_save(self, form):
"""
This checks for failed batch initialization when creating a new batch.
If a failure is detected, the user is redirected to the page for
creating new batches. The assumption here is that the
:meth:`init_batch()` method responsible for indicating the failure will
have set a flash message for the user with more info.
"""
if self.creating and not self.batch_inited:
return HTTPFound(location=self.request.route_url(
'{0}.create'.format(self.route_prefix)))
def post_save_url(self, form): def post_save_url(self, form):
""" """
@ -632,7 +652,8 @@ class FileBatchCrud(BatchCrud):
class BatchRowGrid(BaseGrid): class BatchRowGrid(BaseGrid):
""" """
Base grid view for batch rows, which can be filtered and sorted. Base grid view for batch rows, which can be filtered and sorted. Also it
can delete all rows matching the current list view query.
""" """
@property @property
@ -734,6 +755,22 @@ class BatchRowGrid(BaseGrid):
def tr_class(self, row, i): def tr_class(self, row, i):
pass pass
def render_kwargs(self):
"""
Add the current batch and route prefix to the template context.
"""
return {'batch': self.current_batch(),
'route_prefix': self.route_prefix}
def bulk_delete(self):
"""
Delete all rows matching the current row grid view query.
"""
self.query().delete()
return HTTPFound(location=self.request.route_url(
'{0}.view'.format(self.route_prefix),
uuid=self.request.matchdict['uuid']))
class ProductBatchRowGrid(BatchRowGrid): class ProductBatchRowGrid(BatchRowGrid):
""" """
@ -851,6 +888,11 @@ def defaults(config, batch_grid, batch_crud, row_grid, row_crud, url_prefix,
renderer='/batch/rows.mako', renderer='/batch/rows.mako',
permission='{0}.view'.format(permission_prefix)) permission='{0}.view'.format(permission_prefix))
# Bulk delete batch rows
config.add_route('{0}.rows.bulk_delete'.format(route_prefix), '{0}{{uuid}}/rows/delete'.format(url_prefix))
config.add_view(row_grid, attr='bulk_delete', route_name='{0}.rows.bulk_delete'.format(route_prefix),
permission='{0}.delete'.format(permission_prefix))
# Delete batch row # Delete batch row
config.add_route('{0}.rows.delete'.format(route_prefix), '{0}delete-row/{{uuid}}'.format(url_prefix)) config.add_route('{0}.rows.delete'.format(route_prefix), '{0}delete-row/{{uuid}}'.format(url_prefix))
config.add_view(row_crud, attr='delete', route_name='{0}.rows.delete'.format(route_prefix), config.add_view(row_crud, attr='delete', route_name='{0}.rows.delete'.format(route_prefix),

View file

@ -169,6 +169,8 @@ class SearchableAlchemyGridView(PagedAlchemyGridView):
def modify_query(self, query): def modify_query(self, query):
join_map = self.join_map() join_map = self.join_map()
if not hasattr(self, '_filter_config'):
self._filter_config = self.filter_config()
query = grids.search.filter_query( query = grids.search.filter_query(
query, self._filter_config, self.filter_map(), join_map) query, self._filter_config, self.filter_map(), join_map)
if hasattr(self, '_sort_config'): if hasattr(self, '_sort_config'):

View file

@ -26,6 +26,8 @@ Views for maintaining vendor catalogs
from __future__ import unicode_literals from __future__ import unicode_literals
from pyramid.httpexceptions import HTTPFound
from rattail.db import model from rattail.db import model
from rattail.db.api import get_setting, get_vendor from rattail.db.api import get_setting, get_vendor
from rattail.db.batch.vendorcatalog import VendorCatalog, VendorCatalogRow from rattail.db.batch.vendorcatalog import VendorCatalog, VendorCatalogRow
@ -129,7 +131,12 @@ class VendorCatalogCrud(FileBatchCrud):
def init_batch(self, batch): def init_batch(self, batch):
parser = require_catalog_parser(batch.parser_key) parser = require_catalog_parser(batch.parser_key)
batch.vendor = get_vendor(Session, parser.vendor_key) vendor = get_vendor(Session, parser.vendor_key)
if not vendor:
self.request.session.flash("No vendor setting found in database for key: {0}".format(parser.vendor_key))
return False
batch.vendor = vendor
return True
class VendorCatalogRowGrid(BatchRowGrid): class VendorCatalogRowGrid(BatchRowGrid):