Add running display of stdout.log when executing upgrade

This commit is contained in:
Lance Edgar 2017-08-09 11:44:31 -05:00
parent fbd73a48c4
commit e5b0fe7198
6 changed files with 243 additions and 92 deletions

View file

@ -0,0 +1,59 @@
/********************************************************************************
* progress.css
*
* Styles for progress bar page.
********************************************************************************/
/******************************
* general
******************************/
#body-wrapper {
position: relative;
}
#wrapper {
height: 60px;
left: 50%;
margin-top: -45px;
margin-left: -350px;
position: absolute;
top: 50%;
width: 700px;
}
/******************************
* progress bar
******************************/
#progress-wrapper {
border-collapse: collapse;
}
#progress {
border-collapse: collapse;
height: 25px;
width: 550px;
}
#complete {
background-color: Gray;
width: 0px;
}
#remaining {
background-color: LightGray;
width: 100%;
}
#percentage {
padding-left: 3px;
min-width: 50px;
width: 50px;
}
#cancel .ui-button-text {
white-space: nowrap;
}

View file

@ -5,98 +5,19 @@
<html style="direction: ltr;" xmlns="http://www.w3.org/1999/xhtml" lang="en-us"> <html style="direction: ltr;" xmlns="http://www.w3.org/1999/xhtml" lang="en-us">
<head> <head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" /> <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<title>Working...</title> <title>${initial_msg or "Working"}...</title>
${core_javascript()} ${core_javascript()}
${h.stylesheet_link(request.static_url('tailbone:static/css/normalize.css'))} ${h.stylesheet_link(request.static_url('tailbone:static/css/normalize.css'))}
${jquery_theme()} ${jquery_theme()}
${h.stylesheet_link(request.static_url('tailbone:static/css/base.css'))} ${h.stylesheet_link(request.static_url('tailbone:static/css/base.css'))}
${h.stylesheet_link(request.static_url('tailbone:static/css/layout.css'))} ${h.stylesheet_link(request.static_url('tailbone:static/css/layout.css'))}
<style type="text/css"> ${h.stylesheet_link(request.static_url('tailbone:static/css/progress.css'))}
${self.update_progress_func()}
#body-wrapper { ${self.extra_styles()}
position: relative; <script type="text/javascript">
}
#wrapper {
height: 60px;
left: 50%;
margin-top: -45px;
margin-left: -350px;
position: absolute;
top: 50%;
width: 700px;
}
#progress-wrapper {
border-collapse: collapse;
}
#progress {
border-collapse: collapse;
height: 25px;
width: 550px;
}
#complete {
background-color: Gray;
width: 0px;
}
#remaining {
background-color: LightGray;
width: 100%;
}
#percentage {
padding-left: 3px;
min-width: 50px;
width: 50px;
}
#cancel .ui-button-text {
white-space: nowrap;
}
</style>
<script language="javascript" type="text/javascript">
var updater = null; var updater = null;
function update_progress() {
$.ajax({
url: '${url('progress', key=progress.key)}?sessiontype=${progress.session.type}',
success: function(data) {
if (data.error) {
location.href = '${cancel_url}';
} else if (data.complete || data.maximum) {
$('#message').html(data.message);
$('#total').html('('+data.maximum_display+' total)');
$('#cancel button').show();
if (data.complete) {
clearInterval(updater);
$('#cancel button').hide();
$('#total').html('done!');
$('#complete').css('width', '100%');
$('#remaining').hide();
$('#percentage').html('100 %');
location.href = data.success_url;
} else {
var width = parseInt(data.value) / parseInt(data.maximum);
width = Math.round(100 * width);
if (width) {
$('#complete').css('width', width+'%');
$('#percentage').html(width+' %');
} else {
$('#complete').css('width', '0.01%');
$('#percentage').html('0 %');
}
$('#remaining').css('width', 'auto');
}
}
},
});
}
updater = setInterval(function() {update_progress()}, 1000); updater = setInterval(function() {update_progress()}, 1000);
$(function() { $(function() {
@ -147,6 +68,52 @@
</div><!-- #wrapper --> </div><!-- #wrapper -->
${self.after_progress()}
</div><!-- #body-wrapper --> </div><!-- #body-wrapper -->
</body> </body>
</html> </html>
<%def name="update_progress_func()">
<script type="text/javascript">
function update_progress() {
$.ajax({
url: '${url('progress', key=progress.key)}?sessiontype=${progress.session.type}',
success: function(data) {
if (data.error) {
location.href = '${cancel_url}';
} else if (data.complete || data.maximum) {
$('#message').html(data.message);
$('#total').html('('+data.maximum_display+' total)');
$('#cancel button').show();
if (data.complete) {
clearInterval(updater);
$('#cancel button').hide();
$('#total').html('done!');
$('#complete').css('width', '100%');
$('#remaining').hide();
$('#percentage').html('100 %');
location.href = data.success_url;
} else {
var width = parseInt(data.value) / parseInt(data.maximum);
width = Math.round(100 * width);
if (width) {
$('#complete').css('width', width+'%');
$('#percentage').html(width+' %');
} else {
$('#complete').css('width', '0.01%');
$('#percentage').html('0 %');
}
$('#remaining').css('width', 'auto');
}
}
},
});
}
</script>
</%def>
<%def name="extra_styles()"></%def>
<%def name="after_progress()"></%def>

View file

@ -0,0 +1,77 @@
## -*- coding: utf-8; -*-
<%inherit file="/progress.mako" />
<%def name="update_progress_func()">
<script type="text/javascript">
function update_progress() {
$.ajax({
url: '${url('upgrades.execute_progress', uuid=instance.uuid)}',
success: function(data) {
if (data.error) {
location.href = '${cancel_url}';
} else {
var stdout = $('.stdout');
var height = $(window).height() - stdout.offset().top - 50;
stdout.height(height);
stdout.append(data.stdout);
stdout.animate({scrollTop: stdout.get(0).scrollHeight - height}, 250);
if (data.complete || data.maximum) {
$('#message').html(data.message);
$('#total').html('('+data.maximum_display+' total)');
$('#cancel button').show();
if (data.complete) {
clearInterval(updater);
$('#cancel button').hide();
$('#total').html('done!');
$('#complete').css('width', '100%');
$('#remaining').hide();
$('#percentage').html('100 %');
location.href = data.success_url;
} else {
var width = parseInt(data.value) / parseInt(data.maximum);
width = Math.round(100 * width);
if (width) {
$('#complete').css('width', width+'%');
$('#percentage').html(width+' %');
} else {
$('#complete').css('width', '0.01%');
$('#percentage').html('0 %');
}
$('#remaining').css('width', 'auto');
}
}
}
}
});
}
</script>
</%def>
<%def name="extra_styles()">
${parent.extra_styles()}
<style type="text/css">
#wrapper {
top: 6em;
}
.stdout {
border: 1px solid Black;
height: 500px;
margin-left: 4.5%;
overflow: auto;
padding: 4px;
position: absolute;
top: 10em;
white-space: nowrap;
width: 90%;
}
</style>
</%def>
<%def name="after_progress()">
<div class="stdout"></div>
</%def>
${parent.body()}

View file

@ -90,12 +90,15 @@ class View(object):
def progress_loop(self, func, items, factory, *args, **kwargs): def progress_loop(self, func, items, factory, *args, **kwargs):
return progress_loop(func, items, factory, *args, **kwargs) return progress_loop(func, items, factory, *args, **kwargs)
def render_progress(self, progress, kwargs): # TODO: this signature seems wonky
def render_progress(self, progress, kwargs, template=None):
""" """
Render the progress page, with given kwargs as context. Render the progress page, with given kwargs as context.
""" """
if not template:
template = '/progress.mako'
kwargs['progress'] = progress kwargs['progress'] = progress
return render_to_response('/progress.mako', kwargs, request=self.request) return render_to_response(template, kwargs, request=self.request)
def file_response(self, path): def file_response(self, path):
""" """

View file

@ -71,8 +71,10 @@ class MasterView(View):
bulk_deletable = False bulk_deletable = False
mergeable = False mergeable = False
downloadable = False downloadable = False
executable = False
cloneable = False cloneable = False
executable = False
execute_progress_template = None
execute_progress_initial_msg = None
supports_mobile = False supports_mobile = False
mobile_creatable = False mobile_creatable = False
@ -841,20 +843,22 @@ class MasterView(View):
model_title = self.get_model_title() model_title = self.get_model_title()
if self.request.method == 'POST': if self.request.method == 'POST':
progress = self.make_execute_progress() progress = self.make_execute_progress(obj)
kwargs = {'progress': progress} kwargs = {'progress': progress}
thread = Thread(target=self.execute_thread, args=(obj.uuid, self.request.user.uuid), kwargs=kwargs) thread = Thread(target=self.execute_thread, args=(obj.uuid, self.request.user.uuid), kwargs=kwargs)
thread.start() thread.start()
return self.render_progress(progress, { return self.render_progress(progress, {
'instance': obj,
'initial_msg': self.execute_progress_initial_msg,
'cancel_url': self.get_action_url('view', obj), 'cancel_url': self.get_action_url('view', obj),
'cancel_msg': "{} execution was canceled".format(model_title), 'cancel_msg': "{} execution was canceled".format(model_title),
}) }, template=self.execute_progress_template)
self.request.session.flash("Sorry, you must POST to execute a {}.".format(model_title), 'error') self.request.session.flash("Sorry, you must POST to execute a {}.".format(model_title), 'error')
return self.redirect(self.get_action_url('view', obj)) return self.redirect(self.get_action_url('view', obj))
def make_execute_progress(self): def make_execute_progress(self, obj):
key = '{}.execute'.format(self.get_grid_key()) key = '{}.execute'.format(self.get_grid_key())
return SessionProgress(self.request, key) return SessionProgress(self.request, key)

View file

@ -43,7 +43,7 @@ from deform import widget as dfwidget
from webhelpers2.html import tags from webhelpers2.html import tags
from tailbone.views import MasterView3 as MasterView from tailbone.views import MasterView3 as MasterView
from tailbone.progress import SessionProgress from tailbone.progress import SessionProgress, get_progress_session
class UpgradeView(MasterView): class UpgradeView(MasterView):
@ -51,9 +51,12 @@ class UpgradeView(MasterView):
Master view for all user events Master view for all user events
""" """
model_class = model.Upgrade model_class = model.Upgrade
executable = True
downloadable = True downloadable = True
cloneable = True cloneable = True
executable = True
execute_progress_template = '/upgrade.mako'
execute_progress_initial_msg = "Upgrading"
labels = { labels = {
'executed_by': "Executed by", 'executed_by': "Executed by",
'status_code': "Status", 'status_code': "Status",
@ -281,6 +284,44 @@ class UpgradeView(MasterView):
upgrade.executed = make_utc() upgrade.executed = make_utc()
upgrade.executed_by = user upgrade.executed_by = user
def execute_progress(self):
upgrade = self.get_instance()
key = '{}.execute'.format(self.get_grid_key())
session = get_progress_session(self.request, key)
if session.get('complete'):
msg = session.get('success_msg')
if msg:
self.request.session.flash(msg)
elif session.get('error'):
self.request.session.flash(session.get('error_msg', "An unspecified error occurred."), 'error')
data = dict(session)
path = self.rattail_config.upgrade_filepath(upgrade.uuid, filename='stdout.log')
offset = session.get('stdout.offset', 0)
size = os.path.getsize(path) - offset
with open(path, 'rb') as f:
f.seek(offset)
chunk = f.read(size)
data['stdout'] = chunk.replace('\n', '<br />')
session['stdout.offset'] = offset + size
session.save()
return data
@classmethod
def defaults(cls, config):
route_prefix = cls.get_route_prefix()
url_prefix = cls.get_url_prefix()
permission_prefix = cls.get_permission_prefix()
model_key = cls.get_model_key()
# execution progress
config.add_route('{}.execute_progress'.format(route_prefix), '{}/{{{}}}/execute/progress'.format(url_prefix, model_key))
config.add_view(cls, attr='execute_progress', route_name='{}.execute_progress'.format(route_prefix),
permission='{}.execute'.format(permission_prefix), renderer='json')
cls._defaults(config)
def includeme(config): def includeme(config):
UpgradeView.defaults(config) UpgradeView.defaults(config)