admin: add an inspect view to workflows (#12996)

This commit is contained in:
Frédéric Péters 2016-08-30 17:24:08 +02:00
parent 8844896547
commit cd3da587b2
5 changed files with 269 additions and 2 deletions

View File

@ -28,7 +28,9 @@ from wcs.categories import Category
from wcs.data_sources import NamedDataSource
from wcs.wscalls import NamedWsCall
from wcs.roles import Role
from wcs.workflows import Workflow, DisplayMessageWorkflowStatusItem, WorkflowCriticalityLevel
from wcs.workflows import (Workflow, DisplayMessageWorkflowStatusItem,
WorkflowCriticalityLevel, WorkflowBackofficeFieldsFormDef)
from wcs.wf.register_comment import RegisterCommenterWorkflowStatusItem
from wcs.wf.wscall import WebserviceCallStatusItem
from wcs.formdef import FormDef
from wcs import fields
@ -2270,6 +2272,63 @@ def test_workflows_wscall_label(pub):
resp = app.get('/backoffice/workflows/%s/status/%s/' % (workflow.id, baz_status.id))
assert 'Webservice Call "foowscallbar"' in resp.body
def test_workflows_inspect_view(pub):
from wcs.workflows import WorkflowVariablesFieldsFormDef
from wcs.wf.form import FormWorkflowStatusItem, WorkflowFormFieldsFormDef
create_superuser(pub)
role = create_role()
Workflow.wipe()
workflow = Workflow(name='foo')
workflow.criticality_levels = [WorkflowCriticalityLevel(name='green')]
workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow)
workflow.backoffice_fields_formdef.fields = [
fields.StringField(id='bo1', label='1st backoffice field',
type='string', varname='backoffice_blah', required=True),
]
workflow.variables_formdef = WorkflowVariablesFieldsFormDef(workflow=workflow)
workflow.variables_formdef.fields.append(fields.StringField(label='Test', type='string'))
foo_status = workflow.add_status(name='foo')
baz_status = workflow.add_status(name='baz')
wscall = WebserviceCallStatusItem()
wscall.parent = baz_status
baz_status.items.append(wscall)
baz_status.backoffice_info_text = '<p>Hello</p>'
display_form = FormWorkflowStatusItem()
display_form.id = '_x'
display_form.formdef = WorkflowFormFieldsFormDef(item=display_form)
display_form.formdef.fields.append(fields.StringField(label='Test', type='string'))
display_form.formdef.fields.append(fields.StringField(label='Test2', type='string'))
display_form.backoffice_info_text = '<p>Foo</p>'
baz_status.items.append(display_form)
display_form.parent = baz_status
ac1 = workflow.add_global_action('Action', 'ac1')
ac1.backoffice_info_text = '<p>Foo</p>'
add_to_journal = RegisterCommenterWorkflowStatusItem()
add_to_journal.id = '_add_to_journal'
add_to_journal.comment = 'HELLO WORLD'
ac1.items.append(add_to_journal)
add_to_journal.parent = ac1
trigger = ac1.triggers[0]
assert trigger.key == 'manual'
trigger.roles = [role.id]
workflow.store()
app = login(get_app(pub))
resp = app.get('/backoffice/workflows/%s/inspect' % workflow.id)
def test_users(pub):
create_superuser(pub)
app = login(get_app(pub))

View File

@ -1312,7 +1312,7 @@ class GlobalActionsDirectory(Directory):
class WorkflowPage(Directory):
_q_exports = ['', 'edit', 'delete', 'newstatus', ('status', 'status_dir'), 'update_order',
'duplicate', 'export', 'svg', ('variables', 'variables_dir'),
'duplicate', 'export', 'svg', ('variables', 'variables_dir'), 'inspect',
('backoffice-fields', 'backoffice_fields_dir'),
'update_actions_order', 'update_criticality_levels_order',
('functions', 'functions_dir'), ('global-actions', 'global_actions_dir'),
@ -1533,6 +1533,86 @@ class WorkflowPage(Directory):
r += self.get_new_status_form()
return r.getvalue()
def expand_workflow_formdef(self, formdef):
r = TemplateIO(html=True)
r += htmltext('<ul>')
for field in formdef.fields:
r += htmltext('<li>')
r += field.label
if getattr(field, 'required', True):
r += htmltext(' (%s)') % _('required')
r += htmltext(' (%s)') % _(field.description)
if field.varname:
r += htmltext(' (<tt>%s</tt>)') % field.varname
r += htmltext('</li>')
r += htmltext('</ul>')
return r.getvalue()
def inspect(self):
self.html_top('%s - %s' % (_('Workflow'), self.workflow.name))
r = TemplateIO(html=True)
r += htmltext('<h2>%s</h2>') % self.workflow.name
r += htmltext('<h2>%s</h2>') % _('Workflow Functions')
r += htmltext('<ul>')
for key, label in (self.workflow.roles or {}).items():
r += htmltext('<li>%s</li>') % label
r += htmltext('</ul>')
if self.workflow.variables_formdef:
r += htmltext('<h2>%s</h2>') % _('Workflow Variables')
r += self.expand_workflow_formdef(self.workflow.variables_formdef)
if self.workflow.backoffice_fields_formdef:
r += htmltext('<h2>%s</h2>') % _('Backoffice Fields')
r += self.expand_workflow_formdef(self.workflow.backoffice_fields_formdef)
if self.workflow.criticality_levels:
r += htmltext('<h2>%s</h2>') % _('Criticality Levels')
r += htmltext('<ul>')
for level in (self.workflow.criticality_levels or []):
r += htmltext('<li>%s</li>') % level.name
r += htmltext('</ul>')
r += htmltext('<h2>%s</h2>') % _('Statuses')
r += htmltext('<div class="expanded-statuses">')
for status in self.workflow.possible_status or []:
r += htmltext('<div class="status" style="border-left-color: #%s;">' %
(getattr(status, 'colour', None) or 'fff'))
r += htmltext('<h3 id="status-%s"><a href="status/%s/">%s</a></h3>') % (
status.id, status.id, status.name)
if status.backoffice_info_text:
r += htmltext('<div>')
r += htmltext(status.backoffice_info_text)
r += htmltext('</div>')
if not status.items:
r += htmltext('<p>%s</p>') % _('No actions in this status.')
for item in status.items or []:
r += htmltext('<h4>%s</h4>') % _(item.description)
r += item.get_parameters_view()
r += htmltext('</div>')
r += htmltext('</div>')
if self.workflow.global_actions:
r += htmltext('<h2>%s</h2>') % _('Global Actions')
r += htmltext('<div class="expanded-statuses">')
for action in self.workflow.global_actions:
r += htmltext('<div class="status">')
r += htmltext('<h3><a href="global-actions/%s/">%s</a></h3>') % (
action.id, action.name)
r += htmltext('<ul>')
for trigger in action.triggers:
r += htmltext('<li>%s</li>') % trigger.render_as_line()
r += htmltext('</ul>')
r += htmltext('</h3>')
for item in action.items or []:
r += htmltext('<h4>%s</h4>') % _(item.description)
r += item.get_parameters_view()
r += htmltext('</div>')
r += htmltext('</div>')
return r.getvalue()
def svg(self):
response = get_response()
response.set_content_type('image/svg+xml')

View File

@ -1380,6 +1380,55 @@ table#listing tr.criticality-level td:first-child {
border-left-width: 5px;
border-left-style: solid;
}
div.expanded-statuses div.status p,
div.expanded-statuses div.status h4 {
margin-left: 1ex;
}
div.expanded-status {
counter-reset: status;
}
div.expanded-statuses div.status {
padding-left: 1ex;
border-left: 5px solid transparent;
border-bottom: 1px dotted #888;
margin-bottom: 1em;
counter-reset: items;
counter-increment: status;
}
div.expanded-statuses div.status h3 {
font-size: 150%;
font-weight: normal;
}
div.expanded-statuses div.status h3 a {
border-bottom-color: transparent;
color: inherit;
}
div.expanded-statuses div.status h3 a:hover {
border-bottom-color: #666;
}
div.expanded-statuses div.status h4::before {
counter-increment: items;
content: counters(status,".") "." counters(items,".") " ";
color: #888;
font-size: 90%;
}
div.expanded-statuses div.status ul span.parameter {
color: #666;
}
pre.wrapping-pre {
white-space: pre-wrap;
max-width: 90%;
}
form div.page {
border: 1px solid #e4e4e4;
padding: 0;

View File

@ -177,4 +177,22 @@ class FormWorkflowStatusItem(WorkflowStatusItem):
formdata.update_workflow_data(workflow_data)
formdata.store()
def get_parameters_view(self):
r = TemplateIO(html=True)
r += super(FormWorkflowStatusItem, self).get_parameters_view()
if self.formdef and self.formdef.fields:
r += htmltext('<p>%s</p>') % _('Form:')
r += htmltext('<ul>')
for field in self.formdef.fields:
r += htmltext('<li>')
r += field.label
if getattr(field, 'required', True):
r += htmltext(' (%s)') % _('required')
r += htmltext(' (%s)') % _(field.description)
if field.varname:
r += htmltext(' (<tt>%s</tt>)') % field.varname
r += htmltext('</li>')
r += htmltext('</ul>')
return r.getvalue()
register_item_class(FormWorkflowStatusItem)

View File

@ -32,6 +32,7 @@ from quixote import get_request, redirect
from qommon.misc import C_, get_as_datetime
from qommon.storage import StorableObject
from qommon.form import *
from qommon.humantime import seconds2humanduration
from qommon import emails, get_cfg, get_logger
from quixote.html import htmltext
import qommon.errors
@ -1537,6 +1538,54 @@ class WorkflowStatusItem(XmlSerialisable):
def get_parameters(self):
return ()
def get_parameters_view(self):
r = TemplateIO(html=True)
form = Form()
parameters = [x for x in self.get_parameters() if getattr(self, x, None) is not None]
self.add_parameters_widgets(form, parameters)
r += htmltext('<ul>')
for parameter in parameters:
r += htmltext('<li>')
widget = form.get_widget(parameter)
r += htmltext('<span class="parameter">%s</span> ') % _('%s:') % widget.get_title()
r += self.get_parameter_view_value(widget, parameter)
r += htmltext('</li>')
r += htmltext('</ul>')
return r.getvalue()
def get_backoffice_info_text_parameter_view_value(self):
return htmltext('<pre>%s</pre>') % self.backoffice_info_text
def get_by_parameter_view_value(self):
return self.render_list_of_roles(self.by)
def get_timeout_parameter_view_value(self):
return seconds2humanduration(int(self.timeout or 0))
def get_status_parameter_view_value(self):
for status in self.parent.parent.possible_status:
if status.id == self.status:
return htmltext('<a href="#status-%s">%s</a>') % (status.id, status.name)
return _('Unknown (%s)') % self.status.id
def get_parameter_view_value(self, widget, parameter):
if hasattr(self, 'get_%s_parameter_view_value' % parameter):
return getattr(self, 'get_%s_parameter_view_value' % parameter)()
value = getattr(self, parameter)
if type(parameter) is bool:
return _('Yes') if value else _('No')
elif hasattr(widget, 'options') and value:
for option in widget.options:
if isinstance(option, tuple):
if option[0] == value:
return option[1]
else:
if option == value:
return option
return '-'
else:
return str(value)
def fill_admin_form(self, form):
self.add_parameters_widgets(form, self.get_parameters())
@ -1916,6 +1965,9 @@ class SendmailWorkflowStatusItem(WorkflowStatusItem):
t.append(role_label)
return ', '.join(t)
def get_to_parameter_view_value(self):
return self.render_list_of_roles_or_emails(self.to)
def render_as_line(self):
if self.to:
return _('Send mail to %s') % self.render_list_of_roles_or_emails(self.to)
@ -1947,6 +1999,9 @@ class SendmailWorkflowStatusItem(WorkflowStatusItem):
validation_function=ComputedExpressionWidget.validate_ezt,
hint=_('Available variables: url, url_status, details, name, number, comment, field_NAME'))
def get_body_parameter_view_value(self):
return htmltext('<pre class="wrapping-pre">%s</pre>') % self.body
def perform(self, formdata):
if not self.to:
return
@ -2198,6 +2253,12 @@ class DisplayMessageWorkflowStatusItem(WorkflowStatusItem):
def get_parameters(self):
return ('message', 'to')
def get_message_parameter_view_value(self):
if self.message.startswith('<'):
return htmltext(self.message)
return htmltext('<pre>%s</pre>') % self.message
register_item_class(DisplayMessageWorkflowStatusItem)