Add way to execute multiple handheld batches at once

This commit is contained in:
Lance Edgar 2017-06-21 17:29:06 -05:00
parent a63f2e3623
commit 5a0fa20e03
2 changed files with 157 additions and 3 deletions

View file

@ -0,0 +1,60 @@
## -*- coding: utf-8; -*-
<%inherit file="/newbatch/index.mako" />
<%def name="extra_javascript()">
${parent.extra_javascript()}
<script type="text/javascript">
var has_execution_options = ${'true' if master.has_execution_options else 'false'};
$(function() {
$('#execute-results-button').click(function() {
var form = $('form[name="execute-results"]');
if (has_execution_options) {
$('#execution-options-dialog').dialog({
title: "Execution Options",
width: 500,
height: 300,
modal: true,
buttons: [
{
text: "Execute",
click: function(event) {
dialog_button(event).button('option', 'label', "Executing, please wait...").button('disable');
form.submit();
}
},
{
text: "Cancel",
click: function() {
$(this).dialog('close');
}
}
]
});
} else {
$(this).button('option', 'label', "Executing, please wait...").button('disable');
form.submit();
}
});
});
</script>
</%def>
<%def name="grid_tools()">
<button type="button" id="execute-results-button">Execute Results</button>
</%def>
${parent.body()}
<div id="execution-options-dialog" style="display: none;">
${h.form(url('{}.execute_results'.format(route_prefix)), name='execute-results')}
${h.csrf_token(request)}
% if master.has_execution_options:
${rendered_execution_options|n}
% endif
${h.end_form()}
</div>

View file

@ -34,6 +34,7 @@ import datetime
import logging
from cStringIO import StringIO
import six
from sqlalchemy import orm
from rattail.db import model, Session as RattailSession
@ -110,7 +111,15 @@ class BatchMasterView(MasterView):
kwargs['rendered_execution_options'] = self.render_execution_options(batch)
return kwargs
def render_execution_options(self, batch):
def template_kwargs_index(self, **kwargs):
kwargs['execute_enabled'] = self.executable()
if kwargs['execute_enabled'] and self.has_execution_options:
kwargs['rendered_execution_options'] = self.render_execution_options()
return kwargs
def render_execution_options(self, batch=None):
if batch is None:
batch = self.model_class
form = self.make_execution_options_form(batch)
kwargs = {
'batch': batch,
@ -434,7 +443,7 @@ class BatchMasterView(MasterView):
def editable_instance(self, batch):
return not bool(batch.executed)
def executable(self, batch):
def executable(self, batch=None):
return self.handler.executable(batch)
def batch_refreshable(self, batch):
@ -460,10 +469,12 @@ class BatchMasterView(MasterView):
# TODO
execution_options_schema = None
def make_execution_options_form(self, batch):
def make_execution_options_form(self, batch=None):
"""
Return a proper Form for execution options.
"""
if batch is None:
batch = self.model_class
defaults = {}
for field in self.execution_options_schema.fields:
key = 'batch.{}.execute_option.{}'.format(batch.batch_key, field)
@ -819,6 +830,82 @@ class BatchMasterView(MasterView):
def get_execute_success_url(self, batch, result, **kwargs):
return self.get_action_url('view', batch)
def execute_results(self):
"""
Execute all batches which are returned from the current index query.
Starts a separate thread for the execution, and displays a progress
indicator page.
"""
if self.request.method == 'POST':
kwargs = {}
if self.has_execution_options:
form = self.make_execution_options_form()
if not form.validate():
raise RuntimeError("Execution options form did not validate")
kwargs.update(form.data)
# cache options to use as defaults next time
for key, value in form.data.iteritems():
self.request.session['batch.{}.execute_option.{}'.format(self.model_class.batch_key, key)] = six.text_type(value)
key = '{}.execute_results'.format(self.model_class.__tablename__)
batches = self.get_effective_data()
kwargs['progress'] = SessionProgress(self.request, key)
thread = Thread(target=self.execute_results_thread, args=(batches, self.request.user.uuid), kwargs=kwargs)
thread.start()
return self.render_progress({
'key': key,
'cancel_url': self.get_index_url(),
'cancel_msg': "Batch execution was canceled",
})
self.request.session.flash("Sorry, you must POST to execute batches", 'error')
return self.redirect(self.get_index_url())
def execute_results_thread(self, batches, user_uuid, progress=None, **kwargs):
"""
Thread target for executing multiple batches with progress indicator.
"""
session = RattailSession()
batches = batches.with_session(session).all()
user = session.query(model.User).get(user_uuid)
try:
result = self.handler.execute_many(batches, user=user, progress=progress, **kwargs)
# If anything goes wrong, rollback and log the error etc.
except Exception as error:
session.rollback()
log.exception("execution failed for batch: {}".format(batch))
session.close()
if progress:
progress.session.load()
progress.session['error'] = True
progress.session['error_msg'] = self.execute_error_message(error)
progress.session.save()
# If no error, check result flag (false means user canceled).
else:
if result:
now = datetime.datetime.utcnow()
for batch in batches:
batch.executed = now
batch.executed_by = user
session.commit()
# TODO: this doesn't always work...?
self.request.session.flash("{} {} were executed".format(
len(batches), self.get_model_title_plural()))
else:
session.rollback()
session.close()
if progress:
progress.session.load()
progress.session['complete'] = True
progress.session['success_url'] = self.get_index_url()
progress.session.save()
def csv(self):
"""
Download batch data as CSV.
@ -913,6 +1000,13 @@ class BatchMasterView(MasterView):
config.add_tailbone_permission(permission_prefix, '{}.execute'.format(permission_prefix),
"Execute {}".format(model_title))
# execute (multiple) batch results
config.add_route('{}.execute_results'.format(route_prefix), '{}/execute-results'.format(url_prefix))
config.add_view(cls, attr='execute_results', route_name='{}.execute_results'.format(route_prefix),
permission='{}.execute_multiple'.format(permission_prefix))
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))