11from django .contrib import admin , messages
22from django .contrib .auth import get_permission_codename
33from django .db import transaction
4+ from django .forms .widgets import Media , MediaAsset , Script
45from django .utils .html import format_html
6+ from django .utils .safestring import mark_safe
57from django .utils .translation import gettext_lazy as t
68
79from . import forms , models
@@ -19,13 +21,13 @@ def rerun(modeladmin, request, queryset):
1921 if succeeded :
2022 messages .warning (
2123 request ,
22- "Only failed tasks can be retried. %s tasks have been skipped" % succeeded ,
24+ f "Only failed tasks can be retried. { succeeded } tasks have been skipped" ,
2325 )
2426 counter = 0
2527 for obj in queryset .not_succeeded ().iterator ():
2628 obj .enqueue ()
2729 counter += 1
28- messages .success (request , "%s tasks have been successfully queued" % counter )
30+ messages .success (request , f" { counter } tasks have been successfully queued" )
2931
3032
3133@admin .action (
@@ -37,8 +39,7 @@ def cancel(modeladmin, request, queryset):
3739 if not_scheduled :
3840 messages .warning (
3941 request ,
40- "Only scheduled tasks can be canceled. %s tasks have been skipped"
41- % not_scheduled ,
42+ f"Only scheduled tasks can be canceled. { not_scheduled } tasks have been skipped" ,
4243 )
4344 queryset .scheduled ().cancel (request .user )
4445 messages .success (request , "Tasks have been successfully canceled" )
@@ -135,6 +136,14 @@ class TaskInlineAdmin(admin.TabularInline):
135136 classes = ["collapse" ]
136137
137138
139+ class CSS (MediaAsset ):
140+ element_template = "<style{attributes}>{path}</style>"
141+
142+ @property
143+ def path (self ):
144+ return mark_safe (self ._path ) # noqa: S308
145+
146+
138147class WorkflowAdmin (VersionAdmin ):
139148 list_filter = (
140149 "modified" ,
@@ -147,13 +156,40 @@ def get_inlines(self, *args, **kwargs):
147156
148157 def get_readonly_fields (self , * args , ** kwargs ):
149158 return [
150- "get_instance_graph_svg " ,
159+ "display_workflow_diagram " ,
151160 * super ().get_readonly_fields (* args , ** kwargs ),
152161 "modified" ,
153162 "created" ,
154163 ]
155164
165+ @admin .display (description = "Workflow Diagram" )
166+ def display_workflow_diagram (self , obj ):
167+ """Display workflow diagram using MermaidJS for client-side rendering."""
168+ if obj .pk :
169+ return mark_safe ( # noqa: S308
170+ f"""<pre class="mermaid" style="width: 100%; display: block">{ obj .get_instance_graph_mermaid ()} </pre>"""
171+ )
172+ return ""
173+
156174 @transaction .atomic ()
157175 def save_model (self , request , obj , form , change ):
158176 super ().save_model (request , obj , form , change )
159177 form .start_next_tasks (request .user )
178+
179+ @property
180+ def media (self ):
181+ return super ().media + Media (
182+ js = [
183+ Script (
184+ "https://cdn.jsdelivr.net/npm/mermaid@latest/dist/mermaid.esm.min.mjs" ,
185+ type = "module" ,
186+ )
187+ ],
188+ css = {
189+ "all" : [
190+ CSS (
191+ ".field-display_workflow_diagram .flex-container > .readonly { flex: 1 }"
192+ )
193+ ]
194+ },
195+ )
0 commit comments