Refactor batch execution options to use colander/deform

This commit is contained in:
Lance Edgar 2018-02-11 22:37:17 -06:00
parent 2cbacd6187
commit dd04459748
12 changed files with 87 additions and 99 deletions

View file

@ -697,7 +697,7 @@ class Form(object):
context = kwargs context = kwargs
context['form'] = self context['form'] = self
context['dform'] = dform context['dform'] = dform
context['form_kwargs'] = {} context.setdefault('form_kwargs', {})
# TODO: deprecate / remove the latter option here # TODO: deprecate / remove the latter option here
if self.auto_disable_save or self.auto_disable: if self.auto_disable_save or self.auto_disable:
context['form_kwargs']['class_'] = 'autodisable' context['form_kwargs']['class_'] = 'autodisable'

View file

@ -261,6 +261,18 @@
that.grid.gridcore(); that.grid.gridcore();
that.element.unmask(); that.element.unmask();
}); });
},
results_count: function(as_text) {
var count = null;
var match = /showing \d+ thru \d+ of (\S+)/.exec(this.element.find('.pager .showing').text());
if (match) {
count = match[1];
if (!as_text) {
count = parseInt(count, 10);
}
}
return count;
} }
}); });

View file

@ -10,8 +10,6 @@
$(function() { $(function() {
$('.grid-wrapper').gridwrapper();
$('#execute-batch').click(function() { $('#execute-batch').click(function() {
if (has_execution_options) { if (has_execution_options) {
$('#execution-options-dialog').dialog({ $('#execution-options-dialog').dialog({

View file

@ -6,8 +6,6 @@
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.batch.js'))} ${h.javascript_link(request.static_url('tailbone:static/js/tailbone.batch.js'))}
<script type="text/javascript"> <script type="text/javascript">
var has_execution_options = ${'true' if master.has_execution_options(batch) else 'false'};
$(function() { $(function() {
$('#save-refresh').click(function() { $('#save-refresh').click(function() {
@ -55,13 +53,3 @@
<div class="form-wrapper"> <div class="form-wrapper">
${form.render()|n} ${form.render()|n}
</div> </div>
<div id="execution-options-dialog" style="display: none;">
${h.form(url('{}.execute'.format(route_prefix), uuid=batch.uuid), name='batch-execution')}
% if master.has_execution_options(batch):
${rendered_execution_options|n}
% endif
${h.end_form()}
</div>

View file

@ -1,3 +0,0 @@
## -*- coding: utf-8 -*-
${form.field_div('action', form.select('action', options=ACTION_OPTIONS), label="Action to Perform")}

View file

@ -12,6 +12,11 @@
$(function() { $(function() {
$('#execute-results-button').click(function() { $('#execute-results-button').click(function() {
var count = $('.grid-wrapper').gridwrapper('results_count');
if (!count) {
alert("There are no batch results to execute.");
return;
}
var form = $('form[name="execute-results"]'); var form = $('form[name="execute-results"]');
if (has_execution_options) { if (has_execution_options) {
$('#execution-options-dialog').dialog({ $('#execution-options-dialog').dialog({
@ -66,16 +71,11 @@ ${parent.body()}
% if master.results_executable and request.has_perm('{}.execute_multiple'.format(permission_prefix)): % if master.results_executable and request.has_perm('{}.execute_multiple'.format(permission_prefix)):
<div id="execution-options-dialog" style="display: none;"> <div id="execution-options-dialog" style="display: none;">
${h.form(url('{}.execute_results'.format(route_prefix)), name='execute-results')}
${h.csrf_token(request)}
<br /> <br />
<p> <p>
Please be advised, you are about to execute multiple batches! Please be advised, you are about to execute multiple batches!
</p> </p>
<br /> <br />
% if master.has_execution_options(batch): ${execute_form.render_deform(form_kwargs={'name': 'execute-results'}, buttons=False)|n}
${rendered_execution_options|n}
% endif
${h.end_form()}
</div> </div>
% endif % endif

View file

@ -3,7 +3,7 @@
<%def name="extra_javascript()"> <%def name="extra_javascript()">
${parent.extra_javascript()} ${parent.extra_javascript()}
${h.javascript_link(request.static_url('tailbone:static/js/tailbone.batch.js'))} ${h.javascript_link(request.static_url('tailbone:static/js/tailbone.batch.js') + '?ver={}'.format(tailbone.__version__))}
<script type="text/javascript"> <script type="text/javascript">
var has_execution_options = ${'true' if master.has_execution_options(batch) else 'false'}; var has_execution_options = ${'true' if master.has_execution_options(batch) else 'false'};
@ -84,13 +84,6 @@ ${rows_grid|n}
% if not batch.executed: % if not batch.executed:
<div id="execution-options-dialog" style="display: none;"> <div id="execution-options-dialog" style="display: none;">
${execute_form.render_deform(form_kwargs={'name': 'batch-execution'}, buttons=False)|n}
${h.form(url('{}.execute'.format(route_prefix), uuid=batch.uuid), name='batch-execution')}
${h.csrf_token(request)}
% if master.has_execution_options(batch):
${rendered_execution_options|n}
% endif
${h.end_form()}
</div> </div>
% endif % endif

View file

@ -43,11 +43,8 @@
% if master.bulk_deletable and request.has_perm('{}.bulk_delete'.format(permission_prefix)): % if master.bulk_deletable and request.has_perm('{}.bulk_delete'.format(permission_prefix)):
$('form[name="bulk-delete"] button').click(function() { $('form[name="bulk-delete"] button').click(function() {
var count = 0; var count = $('.grid-wrapper').gridwrapper('results_count', true);
var match = /showing \d+ thru \d+ of (\S+)/.exec($('.pager .showing').text()); if (count === null) {
if (match) {
count = match[1];
} else {
alert("There don't seem to be any results to delete!"); alert("There don't seem to be any results to delete!");
return; return;
} }

View file

@ -29,6 +29,7 @@ from __future__ import unicode_literals, absolute_import
import os import os
import datetime import datetime
import logging import logging
import tempfile
from cStringIO import StringIO from cStringIO import StringIO
import six import six
@ -39,13 +40,16 @@ from rattail.db import model, Session as RattailSession
from rattail.threads import Thread from rattail.threads import Thread
from rattail.util import load_object, prettify from rattail.util import load_object, prettify
import colander
import deform
from deform import widget as dfwidget
from pyramid import httpexceptions from pyramid import httpexceptions
from pyramid.renderers import render_to_response from pyramid.renderers import render_to_response
from pyramid.response import FileResponse from pyramid.response import FileResponse
from pyramid_simpleform import Form from pyramid_deform import SessionFileUploadTempStore
from webhelpers2.html import HTML, tags from webhelpers2.html import HTML, tags
from tailbone import forms, grids from tailbone import forms2 as forms, grids
from tailbone.db import Session from tailbone.db import Session
from tailbone.views import MasterView from tailbone.views import MasterView
from tailbone.progress import SessionProgress from tailbone.progress import SessionProgress
@ -133,8 +137,9 @@ class BatchMasterView(MasterView):
kwargs['handler'] = self.handler kwargs['handler'] = self.handler
kwargs['execute_title'] = self.get_execute_title(batch) kwargs['execute_title'] = self.get_execute_title(batch)
kwargs['execute_enabled'] = self.instance_executable(batch) kwargs['execute_enabled'] = self.instance_executable(batch)
if kwargs['execute_enabled'] and self.has_execution_options(batch): if kwargs['execute_enabled']:
kwargs['rendered_execution_options'] = self.render_execution_options(batch) url = self.get_action_url('execute', batch)
kwargs['execute_form'] = self.make_execute_form(batch, action_url=url)
return kwargs return kwargs
def allow_worksheet(self, batch): def allow_worksheet(self, batch):
@ -179,22 +184,12 @@ class BatchMasterView(MasterView):
return batch.id_str return batch.id_str
def template_kwargs_index(self, **kwargs): def template_kwargs_index(self, **kwargs):
kwargs['execute_enabled'] = self.instance_executable(None) route_prefix = self.get_route_prefix()
if kwargs['execute_enabled'] and self.has_execution_options(): if self.results_executable:
kwargs['rendered_execution_options'] = self.render_execution_options() url = self.request.route_url('{}.execute_results'.format(route_prefix))
kwargs['execute_form'] = self.make_execute_form(action_url=url)
return kwargs 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,
'form': forms.FormRenderer(form),
}
kwargs = self.get_exec_options_kwargs(**kwargs)
return self.render('exec_options', kwargs)
def get_exec_options_kwargs(self, **kwargs): def get_exec_options_kwargs(self, **kwargs):
return kwargs return kwargs
@ -422,10 +417,6 @@ class BatchMasterView(MasterView):
def template_kwargs_edit(self, **kwargs): def template_kwargs_edit(self, **kwargs):
batch = kwargs['instance'] batch = kwargs['instance']
kwargs['batch'] = batch kwargs['batch'] = batch
kwargs['execute_title'] = self.get_execute_title(batch)
kwargs['execute_enabled'] = self.instance_executable(batch)
if kwargs['execute_enabled'] and self.has_execution_options(batch):
kwargs['rendered_execution_options'] = self.render_execution_options(batch)
return kwargs return kwargs
def mobile_mark_complete(self): def mobile_mark_complete(self):
@ -609,19 +600,28 @@ class BatchMasterView(MasterView):
# TODO # TODO
execution_options_schema = None execution_options_schema = None
def make_execution_options_form(self, batch=None): def make_execute_schema(self):
return self.execution_options_schema()
def make_execute_form(self, batch=None, **kwargs):
""" """
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: route_prefix = self.get_route_prefix()
key = 'batch.{}.execute_option.{}'.format(batch.batch_key, field)
if key in self.request.session: if self.has_execution_options(batch):
defaults[field] = self.request.session[key] if batch is None:
return Form(self.request, schema=self.execution_options_schema, batch = self.model_class
defaults=defaults or None) schema = self.make_execute_schema()
for field in schema:
key = 'batch.{}.execute_option.{}'.format(batch.batch_key, field.name)
if key in self.request.session:
defaults[field.name] = self.request.session[key]
else:
schema = colander.Schema()
return forms.Form(schema=schema, request=self.request, defaults=defaults, **kwargs)
def get_execute_title(self, batch): def get_execute_title(self, batch):
if hasattr(self.handler, 'get_execute_title'): if hasattr(self.handler, 'get_execute_title'):
@ -855,7 +855,8 @@ class BatchMasterView(MasterView):
""" """
batch = self.get_instance() batch = self.get_instance()
query = self.get_effective_row_data(sort=False) query = self.get_effective_row_data(sort=False)
batch.rowcount -= query.count() if batch.rowcount is not None:
batch.rowcount -= query.count()
query.update({'removed': True}, synchronize_session=False) query.update({'removed': True}, synchronize_session=False)
return self.redirect(self.get_action_url('view', batch)) return self.redirect(self.get_action_url('view', batch))
@ -865,17 +866,14 @@ class BatchMasterView(MasterView):
displays a progress indicator page. displays a progress indicator page.
""" """
batch = self.get_instance() batch = self.get_instance()
if self.request.method == 'POST': self.executing = True
form = self.make_execute_form(batch)
if form.validate(newstyle=True):
kwargs = dict(form.validated)
kwargs = {} # cache options to use as defaults next time
if self.has_execution_options(batch): for key, value in form.validated.items():
form = self.make_execution_options_form(batch) self.request.session['batch.{}.execute_option.{}'.format(batch.batch_key, key)] = value
assert form.validate() # TODO
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(batch.batch_key, key)] = value
key = '{}.execute'.format(self.model_class.__tablename__) key = '{}.execute'.format(self.model_class.__tablename__)
progress = SessionProgress(self.request, key) progress = SessionProgress(self.request, key)
@ -888,7 +886,7 @@ class BatchMasterView(MasterView):
'cancel_msg': "Batch execution was canceled.", 'cancel_msg': "Batch execution was canceled.",
}) })
self.request.session.flash("Sorry, you must POST to execute a batch.", 'error') self.request.session.flash("Invalid request: {}".format(form.make_deform_form().error), 'error')
return self.redirect(self.get_action_url('view', batch)) return self.redirect(self.get_action_url('view', batch))
def execute_error_message(self, error): def execute_error_message(self, error):
@ -949,18 +947,13 @@ class BatchMasterView(MasterView):
Starts a separate thread for the execution, and displays a progress Starts a separate thread for the execution, and displays a progress
indicator page. indicator page.
""" """
if self.request.method == 'POST': form = self.make_execute_form()
if form.validate(newstyle=True):
kwargs = dict(form.validated)
kwargs = {} # cache options to use as defaults next time
if self.has_execution_options(): for key, value in form.validated.items():
form = self.make_execution_options_form() self.request.session['batch.{}.execute_option.{}'.format(self.model_class.batch_key, key)] = value
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)] = value
key = '{}.execute_results'.format(self.model_class.__tablename__) key = '{}.execute_results'.format(self.model_class.__tablename__)
batches = self.get_effective_data() batches = self.get_effective_data()
@ -974,7 +967,7 @@ class BatchMasterView(MasterView):
'cancel_msg': "Batch execution was canceled", 'cancel_msg': "Batch execution was canceled",
}) })
self.request.session.flash("Sorry, you must POST to execute batches", 'error') self.request.session.flash("Invalid request: {}".format(form.make_deform_form().error), 'error')
return self.redirect(self.get_index_url()) return self.redirect(self.get_index_url())
def execute_results_thread(self, batches, user_uuid, progress=None, **kwargs): def execute_results_thread(self, batches, user_uuid, progress=None, **kwargs):

View file

@ -31,9 +31,10 @@ import os
from rattail.db import model from rattail.db import model
from rattail.util import OrderedDict from rattail.util import OrderedDict
import formencode as fe import colander
from webhelpers2.html import tags from webhelpers2.html import tags
from tailbone import forms2 as forms
from tailbone.db import Session from tailbone.db import Session
from tailbone.views.batch import FileBatchMasterView from tailbone.views.batch import FileBatchMasterView
@ -44,10 +45,12 @@ ACTION_OPTIONS = OrderedDict([
]) ])
class ExecutionOptions(fe.Schema): class ExecutionOptions(colander.Schema):
allow_extra_fields = True
filter_extra_fields = True action = colander.SchemaNode(
action = fe.validators.OneOf(ACTION_OPTIONS) colander.String(),
validator=colander.OneOf(ACTION_OPTIONS),
widget=forms.widgets.PlainSelectWidget(values=ACTION_OPTIONS.items()))
class HandheldBatchView(FileBatchMasterView): class HandheldBatchView(FileBatchMasterView):
@ -207,6 +210,9 @@ class HandheldBatchView(FileBatchMasterView):
return super(HandheldBatchView, self).get_execute_success_url(batch) return super(HandheldBatchView, self).get_execute_success_url(batch)
def get_execute_results_success_url(self, result, **kwargs): def get_execute_results_success_url(self, result, **kwargs):
if result is True:
# no batches were actually executed
return self.get_index_url()
batch = result batch = result
return self.get_execute_success_url(batch, result, **kwargs) return self.get_execute_success_url(batch, result, **kwargs)

View file

@ -95,6 +95,7 @@ class MasterView(View):
viewing = False viewing = False
editing = False editing = False
deleting = False deleting = False
executing = False
has_pk_fields = False has_pk_fields = False
row_attrs = {} row_attrs = {}

View file

@ -58,23 +58,26 @@ class VendorCatalogsView(FileBatchMasterView):
rows_bulk_deletable = True rows_bulk_deletable = True
grid_columns = [ grid_columns = [
'id',
'created', 'created',
'created_by', 'created_by',
'vendor', 'vendor',
'effective', 'effective',
'filename', 'filename',
'rowcount',
'executed', 'executed',
] ]
form_fields = [ form_fields = [
'id',
'vendor', 'vendor',
'filename', 'filename',
'effective', 'effective',
'created', 'created',
'created_by', 'created_by',
'rowcount',
'executed', 'executed',
'executed_by', 'executed_by',
'rowcount',
] ]
row_grid_columns = [ row_grid_columns = [