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 import logging
from cStringIO import StringIO from cStringIO import StringIO
import six
from sqlalchemy import orm from sqlalchemy import orm
from rattail.db import model, Session as RattailSession from rattail.db import model, Session as RattailSession
@ -110,7 +111,15 @@ class BatchMasterView(MasterView):
kwargs['rendered_execution_options'] = self.render_execution_options(batch) kwargs['rendered_execution_options'] = self.render_execution_options(batch)
return kwargs 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) form = self.make_execution_options_form(batch)
kwargs = { kwargs = {
'batch': batch, 'batch': batch,
@ -434,7 +443,7 @@ class BatchMasterView(MasterView):
def editable_instance(self, batch): def editable_instance(self, batch):
return not bool(batch.executed) return not bool(batch.executed)
def executable(self, batch): def executable(self, batch=None):
return self.handler.executable(batch) return self.handler.executable(batch)
def batch_refreshable(self, batch): def batch_refreshable(self, batch):
@ -460,10 +469,12 @@ class BatchMasterView(MasterView):
# TODO # TODO
execution_options_schema = None 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. Return a proper Form for execution options.
""" """
if batch is None:
batch = self.model_class
defaults = {} defaults = {}
for field in self.execution_options_schema.fields: for field in self.execution_options_schema.fields:
key = 'batch.{}.execute_option.{}'.format(batch.batch_key, field) 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): def get_execute_success_url(self, batch, result, **kwargs):
return self.get_action_url('view', batch) 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): def csv(self):
""" """
Download batch data as CSV. Download batch data as CSV.
@ -913,6 +1000,13 @@ class BatchMasterView(MasterView):
config.add_tailbone_permission(permission_prefix, '{}.execute'.format(permission_prefix), config.add_tailbone_permission(permission_prefix, '{}.execute'.format(permission_prefix),
"Execute {}".format(model_title)) "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 # download rows as CSV
if cls.rows_downloadable: if cls.rows_downloadable:
config.add_route('{}.csv'.format(route_prefix), '{}/{{uuid}}/csv'.format(url_prefix)) config.add_route('{}.csv'.format(route_prefix), '{}/{{uuid}}/csv'.format(url_prefix))