Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 10 additions & 60 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,33 +1,10 @@
name: CI

on:
push:
branches:
- main
pull_request:

jobs:

lint:
runs-on: ubuntu-latest
strategy:
matrix:
lint-command:
- bandit -r . -x ./tests
- black --check --diff .
- flake8 .
- isort --check-only --diff .
- pydocstyle .
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
with:
python-version: "3.x"
cache: 'pip'
cache-dependency-path: pyproject.toml
- run: python -m pip install .[lint]
- run: ${{ matrix.lint-command }}

docs:
runs-on: ubuntu-latest
steps:
Expand All @@ -39,7 +16,6 @@ jobs:
- run: python -m pip install sphinxcontrib-spelling
- run: python -m pip install -e '.[docs]'
- run: python -m sphinx -W -b spelling docs docs/_build

dist:
runs-on: ubuntu-latest
steps:
Expand All @@ -50,34 +26,35 @@ jobs:
- run: python -m pip install --upgrade pip build wheel twine readme-renderer
- run: python -m build --sdist --wheel
- run: python -m twine check dist/*

PyTest:
needs:
- dist
- lint
runs-on: ubuntu-latest
strategy:
matrix:
python-version:
- "3.9"
- "3.10"
- "3.11"
- "3.12"
- "3.13"
- "3.14"
django-version:
- "3.2"
- "4.0"
- "4.1"
- "5.2"
extras:
- "test"
include:
- python-version: "3.x"
django-version: "4.1"
django-version: "5.2"
extras: "test,dramatiq"
- python-version: "3.x"
django-version: "4.1"
django-version: "5.2"
extras: "test,celery"
- python-version: "3.x"
django-version: "4.1"
django-version: "5.2"
extras: "test,reversion"
- python-version: "3.x"
django-version: "5.2"
extras: "test,graphiz"
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in extras specification: 'graphiz' should be 'graphviz' to match the optional dependency name defined in pyproject.toml.

Suggested change
extras: "test,graphiz"
extras: "test,graphviz"

Copilot uses AI. Check for mistakes.
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
Expand All @@ -88,30 +65,3 @@ jobs:
- run: python -m pip install -e .[${{ matrix.extras }}]
- run: python -m pytest
- uses: codecov/codecov-action@v5

analyze:
name: CodeQL
needs: [ PyTest ]
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ python ]
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
queries: +security-and-quality
- name: Autobuild
uses: github/codeql-action/autobuild@v4
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
with:
category: "/language:${{ matrix.language }}"
3 changes: 0 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
name: Release

on:
release:
types: [published]

jobs:

PyPi:
runs-on: ubuntu-latest
steps:
Expand Down
53 changes: 53 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: trailing-whitespace
- id: check-merge-conflict
- id: check-ast
- id: check-toml
- id: check-yaml
- id: debug-statements
- id: end-of-file-fixer
- id: name-tests-test
args: ['--pytest-test-first']
exclude: ^tests/(testapp/|manage.py$)
- id: no-commit-to-branch
args: [--branch, main]
- repo: https://github.com/asottile/pyupgrade
rev: v3.21.1
hooks:
- id: pyupgrade
- repo: https://github.com/adamchainz/django-upgrade
rev: 1.29.1
hooks:
- id: django-upgrade
- repo: https://github.com/hukkin/mdformat
rev: 1.0.0
hooks:
- id: mdformat
additional_dependencies:
- mdformat-ruff
- mdformat-footnote
- mdformat-gfm
- mdformat-gfm-alerts
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.4
hooks:
- id: ruff-check
args: [--fix, --exit-non-zero-on-fix]
- id: ruff-format
- repo: https://github.com/google/yamlfmt
rev: v0.20.0
hooks:
- id: yamlfmt
- repo: https://github.com/djlint/djLint
rev: v1.36.4
hooks:
- id: djlint-reformat-django
ci:
autoupdate_schedule: weekly
skip:
- no-commit-to-branch
4 changes: 0 additions & 4 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
# .readthedocs.yaml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details

version: 2

build:
os: ubuntu-20.04
tools:
python: "3.11"
apt_packages:
- graphviz

sphinx:
configuration: docs/conf.py

python:
install:
- method: pip
Expand Down
1 change: 0 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ to add code to their runtime environment that they don't need.
All features need to be tested. A CI suite should be in place. Running and
writing tests should be reasonably accessible for first time contributors.


## Release

We follow [semantic versioning](https://semver.org/). To release a new version
Expand Down
11 changes: 7 additions & 4 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def linkcode_resolve(domain, info):
pass
try:
lines, first_line = inspect.getsourcelines(item)
lineno = "#L%d-L%s" % (first_line, first_line + len(lines) - 1)
lineno = f"#L{first_line:d}-L{first_line + len(lines) - 1:d}"
except (TypeError, OSError):
pass
return (
Expand Down Expand Up @@ -79,6 +79,9 @@ def linkcode_resolve(domain, info):

graphviz_output_format = "svg"

inheritance_graph_attrs = dict(
rankdir="TB", size='"6.0, 8.0"', fontsize=14, ratio="compress"
)
inheritance_graph_attrs = {
"rankdir": "TB",
"size": '"6.0, 8.0"',
"fontsize": 14,
"ratio": "compress",
}
1 change: 0 additions & 1 deletion docs/tutorial/testing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,3 @@ running workflow. You can test any other task by simply creating the
workflow and task in during test setup. In those cases you will need
pass the task primary key. You can find more information about this
in the :ref:`URLs documentation<topic-urls>`.

46 changes: 41 additions & 5 deletions joeflow/admin.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from django.contrib import admin, messages
from django.contrib.auth import get_permission_codename
from django.db import transaction
from django.forms.widgets import Media, MediaAsset, Script
from django.utils.html import format_html
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as t

from . import forms, models
Expand All @@ -19,13 +21,13 @@ def rerun(modeladmin, request, queryset):
if succeeded:
messages.warning(
request,
"Only failed tasks can be retried. %s tasks have been skipped" % succeeded,
f"Only failed tasks can be retried. {succeeded} tasks have been skipped",
)
counter = 0
for obj in queryset.not_succeeded().iterator():
obj.enqueue()
counter += 1
messages.success(request, "%s tasks have been successfully queued" % counter)
messages.success(request, f"{counter} tasks have been successfully queued")


@admin.action(
Expand All @@ -37,8 +39,7 @@ def cancel(modeladmin, request, queryset):
if not_scheduled:
messages.warning(
request,
"Only scheduled tasks can be canceled. %s tasks have been skipped"
% not_scheduled,
f"Only scheduled tasks can be canceled. {not_scheduled} tasks have been skipped",
)
queryset.scheduled().cancel(request.user)
messages.success(request, "Tasks have been successfully canceled")
Expand Down Expand Up @@ -135,6 +136,14 @@ class TaskInlineAdmin(admin.TabularInline):
classes = ["collapse"]


class CSS(MediaAsset):
element_template = "<style{attributes}>{path}</style>"

@property
def path(self):
return mark_safe(self._path) # noqa: S308


class WorkflowAdmin(VersionAdmin):
list_filter = (
"modified",
Expand All @@ -147,13 +156,40 @@ def get_inlines(self, *args, **kwargs):

def get_readonly_fields(self, *args, **kwargs):
return [
"get_instance_graph_svg",
"display_workflow_diagram",
*super().get_readonly_fields(*args, **kwargs),
"modified",
"created",
]

@admin.display(description="Workflow Diagram")
def display_workflow_diagram(self, obj):
"""Display workflow diagram using MermaidJS for client-side rendering."""
if obj.pk:
return mark_safe( # noqa: S308
f"""<pre class="mermaid" style="width: 100%; display: block">{obj.get_instance_graph_mermaid()}</pre>"""
)
return ""

@transaction.atomic()
def save_model(self, request, obj, form, change):
super().save_model(request, obj, form, change)
form.start_next_tasks(request.user)

@property
def media(self):
return super().media + Media(
js=[
Script(
"https://cdn.jsdelivr.net/npm/mermaid@latest/dist/mermaid.esm.min.mjs",
type="module",
)
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MermaidJS module is loaded but never initialized. When using MermaidJS version 10+, you need to explicitly initialize it. The script should include initialization code like:

import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@latest/dist/mermaid.esm.min.mjs';
mermaid.initialize({ startOnLoad: true });

Without initialization, the diagrams won't be rendered in the browser. Consider adding a separate initialization script or updating the Script element to include initialization.

Suggested change
)
),
Script(
"""
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@latest/dist/mermaid.esm.min.mjs';
mermaid.initialize({ startOnLoad: true });
""",
type="module",
),

Copilot uses AI. Check for mistakes.
],
css={
"all": [
CSS(
".field-display_workflow_diagram .flex-container > .readonly { flex: 1 }"
)
]
},
)
3 changes: 1 addition & 2 deletions joeflow/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@


class JoeflowAppConfig(AppConf):
"""
List of available settings.
"""List of available settings.

To change the default values just set the setting in your settings file.
"""
Expand Down
5 changes: 2 additions & 3 deletions joeflow/management/commands/render_workflow_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@ def handle(self, *args, **options):
opt = workflow._meta
if verbosity > 0:
self.stdout.write(
"Rendering graph for '%s.%s'… "
% (opt.app_label, opt.model_name),
f"Rendering graph for '{opt.app_label}.{opt.model_name}'… ",
ending="",
)
filename = f"{opt.app_label}_{workflow.__name__}".lower()
Expand All @@ -65,5 +64,5 @@ def handle(self, *args, **options):
self.stdout.write("Done!", self.style.SUCCESS)
else:
self.stderr.write(
"%r is not a Workflow subclass" % workflow, self.style.WARNING
f"{workflow!r} is not a Workflow subclass", self.style.WARNING
)
Loading
Loading