Add way to execute multiple handheld batches at once
This commit is contained in:
parent
a63f2e3623
commit
5a0fa20e03
60
tailbone/templates/batch/handheld/index.mako
Normal file
60
tailbone/templates/batch/handheld/index.mako
Normal 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>
|
|
@ -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))
|
||||||
|
|
Loading…
Reference in a new issue