Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create graphs for fsm models #6224

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion bluebottle/activities/states.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ def should_auto_approve(self):
)

reject = Transition(
AllStates(),
[submitted, open, succeeded],
rejected,
name=_('Reject'),
description=_(
Expand Down
67 changes: 67 additions & 0 deletions bluebottle/fsm/graph.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from graphviz import Digraph

from bluebottle.fsm.triggers import TransitionTrigger
from bluebottle.fsm.utils import clean, setup_instance
from bluebottle.time_based.models import Team

instance = setup_instance(Team)

machine = instance.states
states = []
transitions = []

for state in list(machine.states.values()):
states.append(state.name.capitalize())


for transition in machine.transitions.values():
sources = transition.sources
if not isinstance(sources, (list, tuple)):
sources = (sources,)

for source in sources:
triggers = [
trigger for trigger in instance.triggers.triggers
if isinstance(trigger, TransitionTrigger) and trigger.transition == transition
]
effects = sum([trigger.effects for trigger in triggers], [])
transitions.append((
source.name.capitalize(),
transition.target.name.capitalize(),
transition.name,
[clean(effect(instance).to_html()) for effect in effects]
))

# Create the FSM graph
fsm = Digraph("FSM", filename="fsm_with_multi_effects", format="png")
fsm.attr(rankdir='LR', size='12,8', dpi='600') # Higher resolution and larger canvas

# Add states (nodes)
for state in states:
fsm.node(state, shape='ellipse', style='filled', color='lightblue')

# Add transitions as separate nodes
for source, target, trigger, effects in transitions:
# Create a unique name for the transition node
transition_node = f"transition_{source}_{target}"
fsm.node(
transition_node,
label=f"{trigger}",
shape='diamond',
style='filled',
color='lightyellow'
)

# Connect states to transition nodes
fsm.edge(source, transition_node, style='solid')
fsm.edge(transition_node, target, style='solid')

# If there are effects, add each effect as a separate node
if effects:
for idx, effect in enumerate(effects):
effect_node = f"effect_{source}_{target}_{idx}"
fsm.node(effect_node, label=effect, shape='note', style='filled', color='lightgreen')
fsm.edge(transition_node, effect_node, style='dashed', color='gray')

# Render and save the FSM diagram
fsm.render(view=True)
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,9 @@ def read_file(name):
'elasticsearch==7.17.9',
'geocoder==1.37.0',
'geopy==2.1.0',
'html2text==2020.1.16',
'graphviz==0.20.1',
'gunicorn==19.10.0',
'html2text==2020.1.16',
'html5lib==1.1',
'icalendar==4.0.4',
'importlib_resources==5.12.0',
Expand Down