Skip to content

Commit a8359cf

Browse files
Copilotcodingjoe
andauthored
Make Graphviz optional, use MermaidJS in admin frontend (#143)
* Update version compatibility Django>=5.2 Python>=3.10 * Integrate pre-commit CI and update linting stack --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: codingjoe <[email protected]> Co-authored-by: Johannes Maron <[email protected]>
1 parent cd99481 commit a8359cf

37 files changed

+508
-262
lines changed

.github/workflows/ci.yml

Lines changed: 10 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,10 @@
11
name: CI
2-
32
on:
43
push:
54
branches:
65
- main
76
pull_request:
8-
97
jobs:
10-
11-
lint:
12-
runs-on: ubuntu-latest
13-
strategy:
14-
matrix:
15-
lint-command:
16-
- bandit -r . -x ./tests
17-
- black --check --diff .
18-
- flake8 .
19-
- isort --check-only --diff .
20-
- pydocstyle .
21-
steps:
22-
- uses: actions/checkout@v5
23-
- uses: actions/setup-python@v6
24-
with:
25-
python-version: "3.x"
26-
cache: 'pip'
27-
cache-dependency-path: pyproject.toml
28-
- run: python -m pip install .[lint]
29-
- run: ${{ matrix.lint-command }}
30-
318
docs:
329
runs-on: ubuntu-latest
3310
steps:
@@ -39,7 +16,6 @@ jobs:
3916
- run: python -m pip install sphinxcontrib-spelling
4017
- run: python -m pip install -e '.[docs]'
4118
- run: python -m sphinx -W -b spelling docs docs/_build
42-
4319
dist:
4420
runs-on: ubuntu-latest
4521
steps:
@@ -50,34 +26,35 @@ jobs:
5026
- run: python -m pip install --upgrade pip build wheel twine readme-renderer
5127
- run: python -m build --sdist --wheel
5228
- run: python -m twine check dist/*
53-
5429
PyTest:
5530
needs:
5631
- dist
57-
- lint
5832
runs-on: ubuntu-latest
5933
strategy:
6034
matrix:
6135
python-version:
62-
- "3.9"
6336
- "3.10"
6437
- "3.11"
38+
- "3.12"
39+
- "3.13"
40+
- "3.14"
6541
django-version:
66-
- "3.2"
67-
- "4.0"
68-
- "4.1"
42+
- "5.2"
6943
extras:
7044
- "test"
7145
include:
7246
- python-version: "3.x"
73-
django-version: "4.1"
47+
django-version: "5.2"
7448
extras: "test,dramatiq"
7549
- python-version: "3.x"
76-
django-version: "4.1"
50+
django-version: "5.2"
7751
extras: "test,celery"
7852
- python-version: "3.x"
79-
django-version: "4.1"
53+
django-version: "5.2"
8054
extras: "test,reversion"
55+
- python-version: "3.x"
56+
django-version: "5.2"
57+
extras: "test,graphiz"
8158
steps:
8259
- uses: actions/checkout@v5
8360
- uses: actions/setup-python@v6
@@ -88,30 +65,3 @@ jobs:
8865
- run: python -m pip install -e .[${{ matrix.extras }}]
8966
- run: python -m pytest
9067
- uses: codecov/codecov-action@v5
91-
92-
analyze:
93-
name: CodeQL
94-
needs: [ PyTest ]
95-
runs-on: ubuntu-latest
96-
permissions:
97-
actions: read
98-
contents: read
99-
security-events: write
100-
strategy:
101-
fail-fast: false
102-
matrix:
103-
language: [ python ]
104-
steps:
105-
- name: Checkout
106-
uses: actions/checkout@v5
107-
- name: Initialize CodeQL
108-
uses: github/codeql-action/init@v4
109-
with:
110-
languages: ${{ matrix.language }}
111-
queries: +security-and-quality
112-
- name: Autobuild
113-
uses: github/codeql-action/autobuild@v4
114-
- name: Perform CodeQL Analysis
115-
uses: github/codeql-action/analyze@v4
116-
with:
117-
category: "/language:${{ matrix.language }}"

.github/workflows/release.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
name: Release
2-
32
on:
43
release:
54
types: [published]
6-
75
jobs:
8-
96
PyPi:
107
runs-on: ubuntu-latest
118
steps:

.pre-commit-config.yaml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# See https://pre-commit.com for more information
2+
# See https://pre-commit.com/hooks.html for more hooks
3+
repos:
4+
- repo: https://github.com/pre-commit/pre-commit-hooks
5+
rev: v6.0.0
6+
hooks:
7+
- id: trailing-whitespace
8+
- id: check-merge-conflict
9+
- id: check-ast
10+
- id: check-toml
11+
- id: check-yaml
12+
- id: debug-statements
13+
- id: end-of-file-fixer
14+
- id: name-tests-test
15+
args: ['--pytest-test-first']
16+
exclude: ^tests/(testapp/|manage.py$)
17+
- id: no-commit-to-branch
18+
args: [--branch, main]
19+
- repo: https://github.com/asottile/pyupgrade
20+
rev: v3.21.1
21+
hooks:
22+
- id: pyupgrade
23+
- repo: https://github.com/adamchainz/django-upgrade
24+
rev: 1.29.1
25+
hooks:
26+
- id: django-upgrade
27+
- repo: https://github.com/hukkin/mdformat
28+
rev: 1.0.0
29+
hooks:
30+
- id: mdformat
31+
additional_dependencies:
32+
- mdformat-ruff
33+
- mdformat-footnote
34+
- mdformat-gfm
35+
- mdformat-gfm-alerts
36+
- repo: https://github.com/astral-sh/ruff-pre-commit
37+
rev: v0.14.4
38+
hooks:
39+
- id: ruff-check
40+
args: [--fix, --exit-non-zero-on-fix]
41+
- id: ruff-format
42+
- repo: https://github.com/google/yamlfmt
43+
rev: v0.20.0
44+
hooks:
45+
- id: yamlfmt
46+
- repo: https://github.com/djlint/djLint
47+
rev: v1.36.4
48+
hooks:
49+
- id: djlint-reformat-django
50+
ci:
51+
autoupdate_schedule: weekly
52+
skip:
53+
- no-commit-to-branch

.readthedocs.yaml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
11
# .readthedocs.yaml
22
# Read the Docs configuration file
33
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
4-
54
version: 2
6-
75
build:
86
os: ubuntu-20.04
97
tools:
108
python: "3.11"
119
apt_packages:
1210
- graphviz
13-
1411
sphinx:
1512
configuration: docs/conf.py
16-
1713
python:
1814
install:
1915
- method: pip

CONTRIBUTING.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ to add code to their runtime environment that they don't need.
2727
All features need to be tested. A CI suite should be in place. Running and
2828
writing tests should be reasonably accessible for first time contributors.
2929

30-
3130
## Release
3231

3332
We follow [semantic versioning](https://semver.org/). To release a new version

docs/conf.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def linkcode_resolve(domain, info):
4949
pass
5050
try:
5151
lines, first_line = inspect.getsourcelines(item)
52-
lineno = "#L%d-L%s" % (first_line, first_line + len(lines) - 1)
52+
lineno = f"#L{first_line:d}-L{first_line + len(lines) - 1:d}"
5353
except (TypeError, OSError):
5454
pass
5555
return (
@@ -79,6 +79,9 @@ def linkcode_resolve(domain, info):
7979

8080
graphviz_output_format = "svg"
8181

82-
inheritance_graph_attrs = dict(
83-
rankdir="TB", size='"6.0, 8.0"', fontsize=14, ratio="compress"
84-
)
82+
inheritance_graph_attrs = {
83+
"rankdir": "TB",
84+
"size": '"6.0, 8.0"',
85+
"fontsize": 14,
86+
"ratio": "compress",
87+
}

docs/tutorial/testing.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,3 @@ running workflow. You can test any other task by simply creating the
9090
workflow and task in during test setup. In those cases you will need
9191
pass the task primary key. You can find more information about this
9292
in the :ref:`URLs documentation<topic-urls>`.
93-

joeflow/admin.py

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
from django.contrib import admin, messages
22
from django.contrib.auth import get_permission_codename
33
from django.db import transaction
4+
from django.forms.widgets import Media, MediaAsset, Script
45
from django.utils.html import format_html
6+
from django.utils.safestring import mark_safe
57
from django.utils.translation import gettext_lazy as t
68

79
from . 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+
138147
class 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+
)

joeflow/conf.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55

66

77
class JoeflowAppConfig(AppConf):
8-
"""
9-
List of available settings.
8+
"""List of available settings.
109
1110
To change the default values just set the setting in your settings file.
1211
"""

joeflow/management/commands/render_workflow_graph.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,7 @@ def handle(self, *args, **options):
5353
opt = workflow._meta
5454
if verbosity > 0:
5555
self.stdout.write(
56-
"Rendering graph for '%s.%s'… "
57-
% (opt.app_label, opt.model_name),
56+
f"Rendering graph for '{opt.app_label}.{opt.model_name}'… ",
5857
ending="",
5958
)
6059
filename = f"{opt.app_label}_{workflow.__name__}".lower()
@@ -65,5 +64,5 @@ def handle(self, *args, **options):
6564
self.stdout.write("Done!", self.style.SUCCESS)
6665
else:
6766
self.stderr.write(
68-
"%r is not a Workflow subclass" % workflow, self.style.WARNING
67+
f"{workflow!r} is not a Workflow subclass", self.style.WARNING
6968
)

0 commit comments

Comments
 (0)