Skip to content

Commit f84cf3c

Browse files
Copilotcodingjoe
andcommitted
Use MermaidJS in admin frontend, make graphviz optional
- Removed graphviz from required dependencies - Added graphviz as optional dependency (extras: graphviz, docs) - Added get_graph_mermaid() and get_instance_graph_mermaid() methods - Updated admin to use MermaidJS for client-side diagram rendering - Added admin template with MermaidJS CDN for browser rendering - Reverted all previous Mermaid server-side rendering changes Co-authored-by: codingjoe <[email protected]>
1 parent 864826a commit f84cf3c

File tree

18 files changed

+300
-435
lines changed

18 files changed

+300
-435
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
- uses: actions/setup-python@v6
3636
with:
3737
python-version: "3.x"
38-
- run: sudo apt install -y python3-enchant
38+
- run: sudo apt install -y python3-enchant graphviz
3939
- run: python -m pip install sphinxcontrib-spelling
4040
- run: python -m pip install -e '.[docs]'
4141
- run: python -m sphinx -W -b spelling docs docs/_build
@@ -83,7 +83,7 @@ jobs:
8383
- uses: actions/setup-python@v6
8484
with:
8585
python-version: ${{ matrix.python-version }}
86-
- run: sudo apt install -y redis-server
86+
- run: sudo apt install -y graphviz redis-server
8787
- run: python -m pip install "django==${{ matrix.django-version }}.*"
8888
- run: python -m pip install -e .[${{ matrix.extras }}]
8989
- run: python -m pytest

.readthedocs.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ build:
88
os: ubuntu-20.04
99
tools:
1010
python: "3.11"
11+
apt_packages:
12+
- graphviz
1113

1214
sphinx:
1315
configuration: docs/conf.py

docs/commands.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ render_workflow_graph
99

1010
Render workflow graph to file::
1111

12-
usage: manage.py render_workflow_graph [-h] [-f {svg,mmd,mermaid}] [-d DIRECTORY]
13-
[workflow [workflow ...]]
12+
usage: manage.py render_workflow_graph [-h] [-f {svg,pdf,png}] [-d DIRECTORY]
13+
[-c] [model [model ...]]
1414

1515
Render workflow graph to file.
1616

@@ -20,8 +20,9 @@ Render workflow graph to file::
2020

2121
optional arguments:
2222
-h, --help show this help message and exit
23-
-f {svg,mmd,mermaid}, --format {svg,mmd,mermaid}
23+
-f {svg,pdf,png}, --format {svg,pdf,png}
2424
Output file format. Default: svg
2525
-d DIRECTORY, --directory DIRECTORY
2626
Output directory. Default is current working
2727
directory.
28+
-c, --cleanup Remove dot-files after rendering.

docs/conf.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,14 @@ def linkcode_resolve(domain, info):
7171
),
7272
"dramatiq": ("https://dramatiq.io/", None),
7373
"celery": ("https://docs.celeryproject.org/en/stable/", None),
74+
"graphviz": ("https://graphviz.readthedocs.io/en/stable/", None),
7475
}
7576

7677
spelling_word_list_filename = "spelling_wordlist.txt"
7778
spelling_show_suggestions = True
7879

80+
graphviz_output_format = "svg"
81+
7982
inheritance_graph_attrs = dict(
8083
rankdir="TB", size='"6.0, 8.0"', fontsize=14, ratio="compress"
8184
)

docs/index.rst

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,22 @@ Django_ web framework.
1717
Here is a little sample of what a workflow or process written with joeflow
1818
may look like:
1919

20-
.. code-block:: mermaid
21-
22-
graph LR
23-
checkout(checkout)
24-
has_email[has email]
25-
ship(ship)
26-
end[end]
27-
send_tracking_code[send tracking code]
28-
checkout --> ship
29-
ship --> has_email
30-
has_email --> send_tracking_code
31-
has_email --> end
32-
send_tracking_code --> end
33-
style checkout fill:white,stroke:#000,stroke-width:2px,color:#000
34-
style has_email fill:white,stroke:#000,stroke-width:2px,color:#000
35-
style ship fill:white,stroke:#000,stroke-width:2px,color:#000
36-
style end fill:white,stroke:#000,stroke-width:4px,color:#000
37-
style send_tracking_code fill:white,stroke:#000,stroke-width:2px,color:#000
38-
linkStyle 3 stroke:#888888
20+
.. graphviz::
21+
22+
digraph {
23+
graph [rankdir=LR]
24+
node [fillcolor=white fontname="sans-serif" shape=rect style=filled]
25+
checkout [color=black fontcolor=black style="filled, rounded"]
26+
"has email" [color=black fontcolor=black style=filled]
27+
ship [color=black fontcolor=black style="filled, rounded"]
28+
end [color=black fontcolor=black style=filled peripheries=2]
29+
"send tracking code" [color=black fontcolor=black style=filled]
30+
checkout -> ship
31+
ship -> "has email"
32+
"has email" -> "send tracking code"
33+
"has email" -> end [color="#888888"]
34+
"send tracking code" -> end
35+
}
3936

4037
.. code-block:: python
4138

docs/tutorial/workflow.rst

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,20 @@ user. A human selects the user (or leaves the field blank). If the user is set
88
a welcome emails is being sent. If the user is blank no email will be send and
99
the workflow will end right way.
1010

11-
.. code-block:: mermaid
12-
13-
graph LR
14-
start(start)
15-
send_welcome_email[send welcome email]
16-
end[end]
17-
has_user[has user]
18-
start --> has_user
19-
has_user --> end
20-
has_user --> send_welcome_email
21-
send_welcome_email --> end
22-
style start fill:white,stroke:#000,stroke-width:2px,color:#000
23-
style send_welcome_email fill:white,stroke:#000,stroke-width:2px,color:#000
24-
style end fill:white,stroke:#000,stroke-width:2px,color:#000
25-
style has_user fill:white,stroke:#000,stroke-width:2px,color:#000
11+
.. graphviz::
12+
13+
digraph {
14+
graph [rankdir=LR]
15+
node [fillcolor=white fontname="Georgia, serif" shape=rect style=filled]
16+
start [color=black fontcolor=black style="filled, rounded"]
17+
"send welcome email" [color=black fontcolor=black style=filled]
18+
end [color=black fontcolor=black style=filled]
19+
"has user" [color=black fontcolor=black style=filled]
20+
start -> "has user"
21+
"has user" -> end
22+
"has user" -> "send welcome email"
23+
"send welcome email" -> end
24+
}
2625

2726
Let's start with the data structure or workflow state. We need a model that can
2827
store a user. Like so:

joeflow/admin.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,12 +147,25 @@ def get_inlines(self, *args, **kwargs):
147147

148148
def get_readonly_fields(self, *args, **kwargs):
149149
return [
150-
"get_instance_graph_svg",
150+
"display_workflow_diagram",
151151
*super().get_readonly_fields(*args, **kwargs),
152152
"modified",
153153
"created",
154154
]
155155

156+
@admin.display(description="Workflow Diagram")
157+
def display_workflow_diagram(self, obj):
158+
"""Display workflow diagram using MermaidJS for client-side rendering."""
159+
if obj.pk:
160+
# Get Mermaid diagram syntax
161+
mermaid_syntax = obj.get_instance_graph_mermaid()
162+
# Wrap in div with mermaid class for client-side rendering
163+
return format_html(
164+
'<div class="mermaid-diagram"><div class="mermaid">{}</div></div>',
165+
mermaid_syntax
166+
)
167+
return ""
168+
156169
@transaction.atomic()
157170
def save_model(self, request, obj, form, change):
158171
super().save_model(request, obj, form, change)

joeflow/management/commands/render_workflow_graph.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def add_arguments(self, parser):
1818
"--format",
1919
dest="format",
2020
type=str,
21-
choices=("svg", "mmd", "mermaid"),
21+
choices=("svg", "pdf", "png"),
2222
default="svg",
2323
help="Output file format. Default: svg",
2424
)
@@ -29,12 +29,19 @@ def add_arguments(self, parser):
2929
type=str,
3030
help="Output directory. Default is current working directory.",
3131
)
32-
32+
parser.add_argument(
33+
"-c",
34+
"--cleanup",
35+
dest="cleanup",
36+
action="store_true",
37+
help="Remove dot-files after rendering.",
38+
)
3339

3440
def handle(self, *args, **options):
3541
workflows = options["workflow"]
3642
verbosity = options["verbosity"]
3743
file_format = options["format"]
44+
cleanup = options["cleanup"]
3845
directory = options.get("directory", None)
3946

4047
workflows = [
@@ -52,7 +59,8 @@ def handle(self, *args, **options):
5259
)
5360
filename = f"{opt.app_label}_{workflow.__name__}".lower()
5461
graph = workflow.get_graph()
55-
graph.render(filename=filename, directory=directory, format=file_format)
62+
graph.format = file_format
63+
graph.render(filename=filename, directory=directory, cleanup=cleanup)
5664
if verbosity > 0:
5765
self.stdout.write("Done!", self.style.SUCCESS)
5866
else:

0 commit comments

Comments
 (0)