<jupyter_start><jupyter_text>🦌 Make the ELK App Interactive 🕹️This notebook shows how you can make the ELK App work dynamically with various types of graphs<jupyter_code>import random

import ipywidgets as W
import networkx as nx

from pprint import pprint
from collections import defaultdict

import elk
from elk import nx as elknx
elk_app = elk.Elk(
    style = {" ": {'height': "100%"}},
    transformer=elknx.transformer.XELK(),
)
elk_app
def make_random_forest(number_of_nodes, hierarchy_roots=1, seed=None):
    """ Create a random directed graph that meets NetworkX's forest criteria """
    if seed is not None:
        random.seed(seed)

    if hierarchy_roots < 1:
        return None

    unassigned = set(range(number_of_nodes))
    assigned = set(random.sample(unassigned, hierarchy_roots))
    unassigned -= assigned

    tree_edges = []
    while unassigned:
        node = random.sample(unassigned, 1)[0]
        tree_edges.append((random.sample(assigned, 1)[0], node))
        unassigned -= {node}
        assigned |= {node}

    return nx.DiGraph(tree_edges)


def find_all(obj, key):
    """ Find all values of a given key in a dictionary"""
    if not isinstance(obj, dict):
        return

    for k, value in obj.items():
        if k == key:
            yield value
        if isinstance(value, dict):
            for result in find_all(value, key):
                yield result
        elif isinstance(value, (tuple, list, set)):
            for item in value:
                for result in find_all(item, key):
                    yield result


@W.interact
def make_graph(
    number_of_nodes=(5, 20),
    percent_of_edges=(1, 99),
    hierarchy_roots=(0, 5),
    seed=(0, 1024),
    debug=False,
    fit=False,
    padding=(0, 100),
    port_scale=(1, 20),
    text_scale=(1, 20),
    label_offset=(1, 10),
):
    hierarchy = make_random_forest(
        number_of_nodes=number_of_nodes,
        hierarchy_roots=hierarchy_roots,
        seed=seed,
    )

    number_of_edges = max(1, int(number_of_nodes * 0.01 * percent_of_edges))

    graph = nx.generators.random_graphs.barabasi_albert_graph(
        n=number_of_nodes,
        m=number_of_edges,
        seed=seed,
    )
    
    for edge in sorted(graph.edges):
        graph.edges[edge]["sourcePort"] = edge
        graph.edges[edge]["targetPort"] = edge
    
    elk_app.transformer.port_scale = port_scale
    elk_app.transformer.label_offset = label_offset
    elk_app.transformer.text_scale = text_scale
    elk_app.transformer.source = (graph, hierarchy)
    if fit:
        elk_app.diagram.fit('root', padding=padding)
    
    if debug:
        counter = defaultdict(list)
        [counter[src].append(tgt) for src, tgt in graph.edges]
        pprint(dict(counter))
        pprint(sum(list(find_all(elk_app.transformer.value, "edges")), []))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10664335664335664, 'fraction_numerical': 0.013636363636363636, 'mean_word_length': 2.8714479025710418, 'pattern_counts': {'":': 1, '<': 6, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '1', 'dir': 'jupyter-structured-clean-dedup', 'id': '134493', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 974, 'n_tokens_neox': 931, 'n_words': 227, 'path': 'examples/04_Interactive.ipynb', 'repo_name': 'nrbgt/ipyelk', 'size': '2860'}