transitions icon indicating copy to clipboard operation
transitions copied to clipboard

Mermaid graph code generation should be prepared for states whose names have spaces or dashes

Open oEscal opened this issue 6 months ago • 2 comments

Mermaid code does not accept states with spaces or dashes. The code should be prepared for this by, at least, sanitizing the names before generating the Mermaid code.

oEscal avatar Jun 20 '25 21:06 oEscal

I just pushed a version with "_name_to_id" to the feature branch.

This:

from transitions.extensions.diagrams import HierarchicalGraphMachine

states = ['A state with Spaces', 'B-with-dashes', {'name': 'C.with.dots',
                     'final': True,
                     'parallel': [{'name': '1', 'children': ['a', {"name": "b", "final": True}],
                                   'initial': 'a',
                                   'transitions': [['go', 'a', 'b']]},
                                  {'name': '2', 'children': ['a', {"name": "b", "final": True}],
                                   'initial': 'a',
                                   'transitions': [['go', 'a', 'b']]}]}]
transitions = [["start", 'C.with.dots', 'A state with Spaces'], ["", "A state with Spaces", "B-with-dashes"], ["", "B-with-dashes", "C.with.dots"]]
m = HierarchicalGraphMachine(states=states, transitions=transitions, initial="C.with.dots", show_conditions=True,
                             title="Mermaid", graph_engine="mermaid", auto_transitions=False)

m.start()
print(m.get_graph().draw(None))

will result in this:

 ---
Mermaid
---
stateDiagram-v2
  direction LR
  classDef s_default fill:white,color:black
  classDef s_inactive fill:white,color:black
  classDef s_parallel color:black,fill:white
  classDef s_active color:red,fill:darksalmon
  classDef s_previous color:blue,fill:azure
  
  state "A state with Spaces" as A___state___with___Spaces
  Class A___state___with___Spaces s_default
  state "B-with-dashes" as B___with___dashes
  Class B___with___dashes s_default
  state "C.with.dots" as C___with___dots
  C___with___dots --> [*]
  Class C___with___dots s_default
  state C___with___dots {
    state "1" as C___with___dots_1
    state C___with___dots_1 {
      [*] --> C___with___dots_1_a
      state "a" as C___with___dots_1_a
      state "b" as C___with___dots_1_b
      C___with___dots_1_b --> [*]
    }
    --
    state "2" as C___with___dots_2
    state C___with___dots_2 {
      [*] --> C___with___dots_2_a
      state "a" as C___with___dots_2_a
      state "b" as C___with___dots_2_b
      C___with___dots_2_b --> [*]
    }
  }
  
  C___with___dots --> A___state___with___Spaces: start
  A___state___with___Spaces --> B___with___dashes
  B___with___dashes --> C___with___dots
  C___with___dots_1 --> C___with___dots_1_a
  C___with___dots_2 --> C___with___dots_2_a
  C___with___dots_1_a --> C___with___dots_1_b: go
  C___with___dots_2_a --> C___with___dots_2_b: go
  [*] --> C___with___dots

aleneum avatar Jul 03 '25 13:07 aleneum

Some improvements concerning style:

from transitions.extensions.diagrams import HierarchicalGraphMachine

states = ['A state with Spaces', 'B-with-dashes', {'name': 'C.with.dots',
                     'final': True,
                     'parallel': [{'name': '1', 'children': ['a', {"name": "b", "final": True}],
                                   'initial': 'a',
                                   'transitions': [['go', 'a', 'b']]},
                                  {'name': '2', 'children': ['a', {"name": "b", "final": True}],
                                   'initial': 'a',
                                   'transitions': [['go', 'a', 'b']]}]}]
transitions = [["start", 'C.with.dots', 'A state with Spaces'], ["", "A state with Spaces", "B-with-dashes"], ["", "B-with-dashes", "C.with.dots"]]

# remove default and parallel styles
# they will be set according to the chosen mermaid theme
del HierarchicalGraphMachine.style_attributes["node"]["default"]
del HierarchicalGraphMachine.style_attributes["node"]["parallel"]

m = HierarchicalGraphMachine(states=states, transitions=transitions, initial="C.with.dots", show_conditions=True,
                             title="Mermaid", graph_engine="mermaid", auto_transitions=False)

m.start()
print(m.get_graph().draw(None))

Output

---
Mermaid
---
stateDiagram-v2
  direction LR
  classDef s_inactive fill:white,color:black
  classDef s_active color:red,fill:darksalmon
  classDef s_previous color:blue,fill:azure
  
  state "A state with Spaces" as A___state___with___Spaces
  state "B-with-dashes" as B___with___dashes
  state "C.with.dots" as C___with___dots
  C___with___dots --> [*]
  state C___with___dots {
    state "1" as C___with___dots_1
    state C___with___dots_1 {
      [*] --> C___with___dots_1_a
      state "a" as C___with___dots_1_a
      state "b" as C___with___dots_1_b
      C___with___dots_1_b --> [*]
    }
    --
    state "2" as C___with___dots_2
    state C___with___dots_2 {
      [*] --> C___with___dots_2_a
      state "a" as C___with___dots_2_a
      state "b" as C___with___dots_2_b
      C___with___dots_2_b --> [*]
    }
  }
  class B___with___dashes s_previous
  class C___with___dots_1_a, C___with___dots_2_a s_active
  class C___with___dots_2_b, C___with___dots, A___state___with___Spaces, C___with___dots_1_b s_default
  class C___with___dots_2, C___with___dots_1 s_parallel
  
  C___with___dots --> A___state___with___Spaces: start
  A___state___with___Spaces --> B___with___dashes
  B___with___dashes --> C___with___dots
  C___with___dots_1 --> C___with___dots_1_a
  C___with___dots_2 --> C___with___dots_2_a
  C___with___dots_1_a --> C___with___dots_1_b: go
  C___with___dots_2_a --> C___with___dots_2_b: go
  [*] --> C___with___dots

aleneum avatar Jul 03 '25 14:07 aleneum