My Digital Garden Setup
Table of Contents
1. Graph
1.1. Open the org-roam.db and export graph to json
import networkx as nx import pathlib import sqlite3 def to_rellink(inp: str) -> str: return pathlib.Path(inp).stem def build_graph() -> nx.DiGraph: """Build a graph from the org-roam database.""" graph = nx.DiGraph() home = pathlib.Path.home() conn = sqlite3.connect(home / "org/dbs/darwin/" / "org-roam.db") # Query all nodes first nodes = conn.execute("SELECT file, id, title FROM nodes WHERE level = 0 and file not like '%.gpg%' and properties not like '%private%' and properties not like '%PRIVATE%' and properties not like '%personal%' and properties not like '%PERSONAL%';") # A double JOIN to get all nodes that are connected by a link links = conn.execute("SELECT n1.id, nodes.id FROM ((nodes AS n1) " "JOIN links ON n1.id = links.source) " "JOIN (nodes AS n2) ON links.dest = nodes.id " "WHERE links.type = '\"id\"';") # Populate the graph graph.add_nodes_from((n[1], { "label": n[2].strip("\""), "tooltip": n[2].strip("\""), "lnk": to_rellink(n[0]).lower(), "id": n[1].strip("\"") }) for n in nodes) graph.add_edges_from(n for n in links if n[0] in graph.nodes and n[1] in graph.nodes) conn.close() return graph
import itertools import json import sys import networkx as nx import networkx.algorithms.link_analysis.pagerank_alg as pag import networkx.algorithms.community as com from networkx.drawing.nx_pydot import read_dot from networkx.readwrite import json_graph N_COM = 7 # Desired number of communities N_MISSING = 50 # Number of predicted missing links MAX_NODES = 2000 # Number of nodes in the final graph def compute_centrality(dot_graph: nx.DiGraph) -> None: """Add a `centrality` attribute to each node with its PageRank score. """ simp_graph = nx.Graph(dot_graph) central = pag.pagerank(simp_graph) min_cent = min(central.values()) central = {i: central[i] - min_cent for i in central} max_cent = max(central.values()) central = {i: central[i] / max_cent for i in central} nx.set_node_attributes(dot_graph, central, "centrality") sorted_cent = sorted(dot_graph, key=lambda x: dot_graph.nodes[x]["centrality"]) for n in sorted_cent[:-MAX_NODES]: dot_graph.remove_node(n) def compute_communities(dot_graph: nx.DiGraph, n_com: int) -> None: """Add a `communityLabel` attribute to each node according to their computed community. """ simp_graph = nx.Graph(dot_graph) communities = com.girvan_newman(simp_graph) labels = [tuple(sorted(c) for c in unities) for unities in itertools.islice(communities, n_com - 1, n_com)][0] label_dict = {l_key: i for i in range(len(labels)) for l_key in labels[i]} nx.set_node_attributes(dot_graph, label_dict, "communityLabel") def read_and_dump(): sys.stderr.write("Reading graph...") DOT_GRAPH = build_graph() compute_centrality(DOT_GRAPH) compute_communities(DOT_GRAPH, N_COM) sys.stderr.write("Done\n") JS_GRAPH = json_graph.node_link_data(DOT_GRAPH) #sys.stdout.write(json.dumps(JS_GRAPH)) return json.dumps(JS_GRAPH) read_and_dump()
1.2. Use D3.js to render the graph
In addition to the code from https://hugocisneros.com/blog/my-org-roam-notes-workflow/, I added zoom, pan and changed some physics parameters
Further resources to add zoom, pan:
- https://www.d3indepth.com/zoom-and-pan/
- https://observablehq.com/@d3/zoom
- https://d3js.org/d3-zoom#zoom_transform
Physics