admin: add an inspect view to workflows (#12996)
This commit is contained in:
parent
8844896547
commit
cd3da587b2
|
@ -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))
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue