From edaf3946f26c33eb9eeaec62d8314f7e0a6ceb3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Tue, 1 Jun 2021 22:25:07 +0200 Subject: [PATCH] backoffice: add tracing information to inspect page (#54497) --- tests/backoffice_pages/test_all.py | 24 ++++++++++ wcs/backoffice/management.py | 69 +++++++++++++++++++++++++++- wcs/qommon/static/css/dc2/admin.scss | 18 ++++++++ wcs/workflows.py | 29 +++++++++++- 4 files changed, 138 insertions(+), 2 deletions(-) diff --git a/tests/backoffice_pages/test_all.py b/tests/backoffice_pages/test_all.py index 7d99c4851..10c081d86 100644 --- a/tests/backoffice_pages/test_all.py +++ b/tests/backoffice_pages/test_all.py @@ -5064,6 +5064,30 @@ def test_inspect_page_with_related_objects(pub): ) +def test_inspect_page_actions_traces(pub): + create_environment(pub) + create_user(pub, is_admin=True) + + formdef = FormDef.get_by_urlname('form-title') + + formdef.data_class().wipe() + formdata = formdef.data_class()() + formdata.data = {} + formdata.just_created() + formdata.perform_workflow() + formdata.store() + + resp = login(get_app(pub)).get(formdata.get_url(backoffice=True), status=200) + resp = resp.click('Data Inspector') + assert '

Actions Tracing

' in resp + assert [x.text for x in resp.pyquery('#inspect-timeline strong')] == ['Just Submitted', 'New'] + assert [x.text for x in resp.pyquery('#inspect-timeline a.tracing-link') if x.text] == [ + 'Email', + 'Email', + 'Automatic Jump', + ] + + def test_workflow_jump_previous(pub): user = create_user(pub) create_environment(pub) diff --git a/wcs/backoffice/management.py b/wcs/backoffice/management.py index 01cf58a64..86763d1dd 100644 --- a/wcs/backoffice/management.py +++ b/wcs/backoffice/management.py @@ -43,7 +43,7 @@ from wcs.forms.backoffice import FormDefUI from wcs.forms.common import FormStatusPage from wcs.roles import logged_users_role from wcs.variables import LazyFieldVar -from wcs.workflows import WorkflowStatusItem, template_on_formdata +from wcs.workflows import ActionsTracingEvolutionPart, WorkflowStatusItem, item_classes, template_on_formdata from ..qommon import _, emails, errors, ezt, force_str, get_cfg, get_logger, misc, ngettext, ods, sms from ..qommon.admin.emails import EmailsDirectory @@ -3517,6 +3517,73 @@ class FormBackOfficeStatusPage(FormStatusPage): r += htmltext('') r += htmltext('') + has_tracing = False + for evolution in self.filled.evolution or []: + if evolution.parts and any(isinstance(x, ActionsTracingEvolutionPart) for x in evolution.parts): + has_tracing = True + break + + if has_tracing: + action_classes = {x.key: x.description for x in item_classes} + r += htmltext('
') + r += htmltext('

%s

\n') % _('Actions Tracing') + r += htmltext('') + r += htmltext('
') + # markers stack if '_markers_stack' in (self.filled.workflow_data or {}): r += htmltext('
') diff --git a/wcs/qommon/static/css/dc2/admin.scss b/wcs/qommon/static/css/dc2/admin.scss index 7382fbb91..8f7e884c5 100644 --- a/wcs/qommon/static/css/dc2/admin.scss +++ b/wcs/qommon/static/css/dc2/admin.scss @@ -1661,10 +1661,28 @@ ul.form-inspector li code { font-size: 100%; } +ul.form-inspector li span.datetime { + padding: 0 1ex; +} + +ul.form-inspector li a.tracing-link { + display: inline; + padding: 0; + &:hover { + background: transparent; + text-decoration: underline; + } +} + ul.form-inspector li span.status { padding: 0 1ex; } +ul.form-inspector span.event { + padding: 0 1ex; + font-style: italic; +} + ul.form-inspector li div.value { display: block; padding: 0 0 0 1em; diff --git a/wcs/workflows.py b/wcs/workflows.py index f61500d07..ab73d6cd8 100644 --- a/wcs/workflows.py +++ b/wcs/workflows.py @@ -79,7 +79,7 @@ def perform_items(items, formdata, depth=20, event=None): continue if not item.check_condition(formdata): continue - performed_actions.append((datetime.datetime.now(), item.id)) + performed_actions.append((datetime.datetime.now(), item.key, item.id)) try: url = item.perform(formdata) or url except AbortActionException as e: @@ -343,6 +343,33 @@ class ActionsTracingEvolutionPart(EvolutionPart): self.event_args = None self.actions = actions + def get_event_label(self): + return { + 'api-created': _('Created (by API)'), + 'api-post-edit-action': _('Actions after edit action (by API)'), + 'api-trigger': _('API Trigger'), + 'backoffice-created': _('Created (backoffice submission)'), + 'continuation': _('Continuation'), + 'csv-import-created': _('Created (by CSV import)'), + 'edit-action': _('Actions after edit action'), + 'frontoffice-created': _('Created (frontoffice submission)'), + 'global-action-button': _('Click on a global action button'), + 'global-action': _('Global action'), + 'global-action-timeout': _('Global action timeout'), + 'timeout-jump': _('Timeout jump'), + 'workflow-created': _('Created (by workflow action)'), + 'workflow-form-submit': _('Action in workflow form'), + }.get(self.event, self.event) + + def is_global_event(self): + return bool(self.event and self.event.startswith('global-')) + + def get_base_url(self, workflow, status_id): + if self.is_global_event(): + return '%sglobal-actions/%s/' % (workflow.get_admin_url(), self.event_args[0]) + status = workflow.get_status(status_id) + return status.get_admin_url() + class DuplicateGlobalActionNameError(Exception): pass