Flowgraph ############# .. automethod:: d3blocks.d3blocks.D3Blocks.flowgraph Input Data ************ The input is either an edge list (DataFrame) or a Mermaid flowchart (string). The edge list needs the columns ``source`` and ``target``; all other columns are optional. .. code:: python # source target label variant animation # 0 start check None # 1 check pick yes emphasis comet # 2 check backorder no error None # 3 backorder check retry async dots Per-node attributes (``label``, ``shape``, ``group``, ``color``, ``tooltip``) are passed with ``nodes``, and group settings (``label``, ``parent``, ``color``) with ``groups``. * Shapes: ``process``, ``decision``, ``terminal``, ``io``, ``subprocess``, ``database``, ``queue``, ``document``, ``external``, ``circle``. * Edge variants: ``default``, ``emphasis``, ``security``, ``async``, ``error``. Each sets a colour, line style and width; an edge's own ``color`` and ``style`` override it. * Animations: ``pulse``, ``dash``, ``dots``, ``comet`` for the whole chart (``animate=``) or per edge (``animation`` column). Process flow ************ .. code:: python import pandas as pd from d3blocks import D3Blocks edges = pd.DataFrame({ 'source': ['start', 'check', 'check', 'fix'], 'target': ['check', 'done', 'fix', 'check'], 'label': ['', 'yes', 'no', 'retry'], }) nodes = { 'start': {'label': 'Start', 'shape': 'terminal'}, 'check': {'label': 'Valid?', 'shape': 'decision'}, 'fix': {'label': 'Fix input'}, 'done': {'label': 'Done', 'shape': 'terminal'}, } d3 = D3Blocks(frame=False) d3.flowgraph(edges, nodes=nodes, direction='TB') # Change a node and redraw d3.node_properties['fix']['color'] = '#e15759' d3.show() .. raw:: html Mermaid input ************* Node shapes, edge labels, dotted (``-.->``) and thick (``==>``) links, ``&``, chains, nested subgraphs, links to and from subgraphs, node colours from ``style`` / ``classDef`` and edge colours and dashes from ``linkStyle`` are converted. Links to subgraphs ****************** A link may name a whole subgraph, at either end and also between two subgraphs. It is drawn to the subgraph's box border, as Mermaid draws it, with dagre and with ELK. Hovering the group lights the links of the group and of the groups nested in it; the trace walks through it like a node. .. code:: python d3.flowgraph(''' flowchart LR subgraph Workers ["Background workers"] W1[scheduler] W2[sync] end Workers --> PG[(PostgreSQL)] ''') With a DataFrame, a ``source`` or ``target`` that names a group in ``groups=`` (one with member nodes and no entry in ``nodes=``) works the same way: .. code:: python edges = pd.DataFrame({'source': ['Workers'], 'target': ['PG']}) nodes = {'W1': {'group': 'Workers'}, 'W2': {'group': 'Workers'}, 'PG': {'shape': 'database'}} d3.flowgraph(edges, nodes=nodes, groups={'Workers': {'label': 'Background workers'}}) A link to an empty subgraph draws a node with the subgraph's title. A link between a subgraph and itself or its own content raises a ``ValueError``, as there is no box border to draw it to. Mermaid errors ************** All d3blocks Mermaid readers (flowcharts, state diagrams, ``sequence`` and ``erd``) follow one rule: every statement is understood, explicitly ignored, or raises a ``ValueError`` that names the line, before any HTML is written. Nothing is dropped silently. * Ignored: blank lines, ``%%`` comments, ``%%{init}%%`` directives and the accessibility metadata ``accTitle`` / ``accDescr``. * Errors: statements that do not parse (for example ``A[Start] --> B{ok?``), a wrong first statement, an ``end`` without ``subgraph`` (or a ``subgraph`` without ``end``), a link between a subgraph and its own content, a linked subgraph id that is also given a label, shape, ``style`` or ``class``, and Mermaid features without an equivalent here, such as ``click`` and a ``direction`` inside a subgraph. Line numbers count from the first line of the diagram (in Markdown: of the fenced block). .. code:: python d3.flowgraph('flowchart LR\n A[Start] --> B{ok?') # ValueError: Mermaid flowchart line 2 could not be parsed: 'A[Start] --> B{ok?' (...) .. code:: python d3.flowgraph(''' flowchart LR start([Order received]) --> check{In stock?} check -->|yes| pick[Pick & pack] check -- no --> back[[Backorder]] -.->|restocked| check subgraph wh [Warehouse] pick --> ship[/Ship parcel/] end ship ==> done([Delivered]) ''') The diagram can also live in a Markdown file as a fenced ``mermaid`` block, so GitHub renders it too; pass the file path (``.md``, ``.markdown``, ``.mmd`` or ``.mermaid``). From Markdown, the first flowchart block is used. .. code:: python d3.flowgraph('docs/order_process.md') State diagrams ************** Mermaid ``stateDiagram`` and ``stateDiagram-v2`` are read too, so lifecycle diagrams render with the same layout, interaction and export as flowcharts. ``[*]`` becomes one start and one end pseudo-state (small circles), transition labels are kept (everything after the first ``:``), notes are added to the state's tooltip and ``classDef`` / ``class`` colours are applied. State diagrams are laid out top to bottom unless the diagram has a ``direction`` statement or ``direction`` is passed. Composite states, ``<>``, ``state "x" as A`` and ``A : description`` raise a ``ValueError`` naming the line. .. code:: python d3.flowgraph(''' stateDiagram-v2 [*] --> pending : Signal created pending --> awaiting_approval : Risk checks pass pending --> cancelled : Strategy disabled awaiting_approval --> approved : Operator approves awaiting_approval --> rejected : Operator rejects approved --> executed : Order sent to broker executed --> [*] rejected --> [*] cancelled --> [*] note right of awaiting_approval Agent-generated signals enter here directly end note classDef review fill:#e0f2fe,stroke:#0284c7 class awaiting_approval review ''') From Markdown, the first flowchart or state diagram block is used; other diagram types are skipped. Architecture diagram ******************** Groups can be nested with ``parent``. ``layout='elk'`` uses ELK's layered layout with orthogonal edge routing; it suits nested architecture diagrams but adds about 1.5 MB to the HTML file. .. code:: python groups = { 'Cloud': {'label': 'Cloud account'}, 'VPC': {'parent': 'Cloud'}, 'App subnet': {'parent': 'VPC'}, 'Data subnet': {'parent': 'VPC'}, } nodes = { 'api': {'label': 'API', 'group': 'App subnet'}, 'worker': {'label': 'Worker', 'group': 'App subnet'}, 'db': {'label': 'PostgreSQL', 'shape': 'database', 'group': 'Data subnet'}, 'bus': {'label': 'Event bus', 'shape': 'queue', 'group': 'Data subnet'}, 'users': {'label': 'Users', 'shape': 'circle'}, } edges = pd.DataFrame({ 'source': ['users', 'api', 'api', 'bus'], 'target': ['api', 'db', 'bus', 'worker'], 'label': ['HTTPS', 'SQL', 'publish', 'consume'], 'variant': ['emphasis', 'default', 'async', 'async'], }) d3.flowgraph(edges, nodes=nodes, groups=groups, layout='elk', animate='dots') Jupyter and Streamlit ********************* In a notebook, pass ``notebook=True``; the chart is shown inline, isolated in an iframe sized by ``figsize`` (default: full width x 650 px). See ``notebooks/flowgraph.ipynb``. .. code:: python d3 = D3Blocks(frame=False) d3.flowgraph(edges, nodes=nodes, notebook=True, filepath=None, figsize=[None, 520]) In Streamlit, generate the HTML with ``return_html=True`` and embed it with ``st.iframe``. ``streamlit/flowgraph_streamlit_demo.py`` edits a Mermaid flowchart in the sidebar and re-renders it with the chosen layout, direction and animation. .. code:: python html = D3Blocks().flowgraph(flowchart, filepath=None, showfig=False, return_html=True) st.iframe(html, height=700, width="stretch") Walkthrough *********** ``steps`` adds a captioned walkthrough. **▶ Walk through** in the top panel opens a caption bar with the step title, the caption, "Step 3 of 9", a row of step stops and Prev / Play / Next. Each step highlights its nodes and edges, dims the rest and zooms to them. Keys: ← and → move, Space plays or pauses, Esc closes. Play advances every 3 seconds and stops on the last step. .. code:: python steps = [ {'title': 'Check', 'caption': 'Every order is validated first.', 'highlight': {'nodes': ['check'], 'edges': [('start', 'check')]}}, {'title': 'Retry', 'caption': 'Invalid input is fixed and checked again.', 'highlight': {'edges': [('check', 'fix'), ('fix', 'check')]}}, ] d3.flowgraph(edges, nodes=nodes, steps=steps) Without ``steps`` the button is **▶ Trace**, the uncaptioned walk through the flow. Unknown nodes or edges and unsupported keys raise a ``ValueError`` with the step index. The same walkthrough is used by ``sequence`` and ``erd``. Theme ***** The theme button (last on the right of the top panel) switches between dark and light at runtime. ``dark_mode=True`` (default) starts dark, ``False`` starts light, and ``dark_mode='auto'`` follows the operating system theme, also when it changes, until the theme button is used. In ``'auto'`` mode a ``?theme=light`` or ``?theme=dark`` URL parameter picks the theme. Exports keep the theme shown. Interaction *********** * Hover a node to trace everything upstream and downstream; click to pin. * **▶ Trace** walks through the flow step by step (from the pinned node, or from all start nodes); with ``steps`` it is **▶ Walk through**, the captioned walkthrough. * Drag nodes; edges and groups follow. **Reset layout** recomputes the layout. * The Layout panel changes direction, spacing, font size, edge curve and animation. * The Legend panel lists groups and the edge variants in use. * Export to SVG, PNG or JSON (including computed positions). .. include:: add_bottom.add