Experiment with centered graph drawing.
This commit is contained in:
parent
b248f507c6
commit
49a5ec3df2
125
scripts/gen-centered-graph.py
Normal file
125
scripts/gen-centered-graph.py
Normal file
@ -0,0 +1,125 @@
|
||||
import requests
|
||||
import sys
|
||||
|
||||
url = 'http://localhost:8000/notes/graph.json'
|
||||
reference_node = sys.argv[1]
|
||||
out = sys.argv[2]
|
||||
|
||||
g = requests.get(url).json()
|
||||
centered_graph = { reference_node: g[reference_node] }
|
||||
del g[reference_node]
|
||||
new_nodes = True
|
||||
|
||||
in_emacs_tree = {
|
||||
reference_node: set(),
|
||||
}
|
||||
|
||||
while new_nodes:
|
||||
new_nodes = False
|
||||
removed = set()
|
||||
for k, v in g.items():
|
||||
for link in v["links"]:
|
||||
if link["target"].startswith("id:"):
|
||||
link["target"] = link["target"][3:]
|
||||
if link['target'] in centered_graph and link.get('relation') == 'in':
|
||||
centered_graph[k] = v
|
||||
|
||||
for l in v["links"]:
|
||||
if l.get('relation') == 'in':
|
||||
t = l['target']
|
||||
if t.startswith("id:"):
|
||||
t = t[3:]
|
||||
|
||||
if '[' in t:
|
||||
# Special case, to be handled on org_rw
|
||||
continue
|
||||
|
||||
if t not in in_emacs_tree:
|
||||
in_emacs_tree[t] = set()
|
||||
in_emacs_tree[t].add(k)
|
||||
|
||||
v['links'] = [
|
||||
l for l in v["links"]
|
||||
if l.get('relation') != 'in'
|
||||
]
|
||||
|
||||
|
||||
removed.add(k)
|
||||
new_nodes = True
|
||||
break
|
||||
for k in removed:
|
||||
del g[k]
|
||||
|
||||
in_emacs = set(centered_graph.keys())
|
||||
|
||||
|
||||
# One more round for the rest, not requiring "in"
|
||||
for k, v in g.items():
|
||||
for link in v["links"]:
|
||||
if link["target"].startswith("id:"):
|
||||
link["target"] = link["target"][3:]
|
||||
if link['target'] in in_emacs:
|
||||
centered_graph[k] = v
|
||||
removed.add(k)
|
||||
|
||||
g = centered_graph
|
||||
|
||||
f = open('graph.dot', 'wt')
|
||||
f.write('digraph {\n')
|
||||
# f.write('bgcolor="#222222"\n')
|
||||
# f.write('fontcolor="#ffffff"\n')
|
||||
f.write('maxiter=1000\n')
|
||||
f.write('splines=curved\n')
|
||||
# f.write('splines=spline\n') # Not supported with edges to cluster
|
||||
f.write('node[shape=rect]\n')
|
||||
# f.write('edge[color="#ffffff"]\n')
|
||||
|
||||
def draw_subgraph(node_id):
|
||||
f.write("subgraph cluster_{} {{\n".format(node_id.replace("-", "_")))
|
||||
f.write('URL="./{}.node.html"\n'.format(node_id))
|
||||
# f.write('color="#ffffff"\n')
|
||||
|
||||
f.write("label=\"{}\"\n".format(g[node_id]['title'].replace("\"", "'")))
|
||||
f.write("\n")
|
||||
|
||||
# print("T: {}".format(in_emacs_tree), file=sys.stderr)
|
||||
for k in in_emacs_tree[node_id]:
|
||||
v = g[k]
|
||||
print("_" + k.replace("-", "_") + "[label=\"" + v["title"].replace("\"", "'") + "\", URL=\"" + k + ".node.html\"];", file=f)
|
||||
|
||||
if k in in_emacs_tree:
|
||||
draw_subgraph(k)
|
||||
|
||||
f.write("\n}")
|
||||
|
||||
draw_subgraph(reference_node)
|
||||
|
||||
for k, v in g.items():
|
||||
if k not in in_emacs:
|
||||
print("_" + k.replace("-", "_") + "[label=\"" + v["title"].replace("\"", "'") + "\", URL=\"" + k + ".node.html\"];", file=f)
|
||||
|
||||
for k, v in g.items():
|
||||
for link in v["links"]:
|
||||
if link["target"].startswith("id:"):
|
||||
link["target"] = link["target"][3:]
|
||||
|
||||
if '[' in link['target']:
|
||||
# Special case, to be handled on org_rw
|
||||
continue
|
||||
if link['target'] not in g:
|
||||
# Irrelevant
|
||||
continue
|
||||
if link['target'] in in_emacs_tree:
|
||||
t = 'cluster_{}'.format(link['target'].replace("-", "_"))
|
||||
else:
|
||||
t = "_" + link["target"].replace("-", "_")
|
||||
print("_" + k.replace("-", "_") + "->" + t, file=f)
|
||||
|
||||
f.write('}\n')
|
||||
# dot graph.dot -Tsvg graph.svg
|
||||
|
||||
f.close()
|
||||
|
||||
import subprocess
|
||||
subprocess.call("fdp graph.dot -Tsvg -o '{}'".format(out), shell=True)
|
||||
# return "graph.svg"
|
@ -336,6 +336,7 @@ def regen_all(src_top, dest_top, *, docs=None, db=None):
|
||||
os.makedirs(attachments_dir, exist_ok=True)
|
||||
for base in base_dirs:
|
||||
data_dir = os.path.join(src_top, base, 'data')
|
||||
logging.info("Copying attachments from: {}".format(data_dir))
|
||||
if not os.path.exists(data_dir):
|
||||
continue
|
||||
for subdir in os.listdir(data_dir):
|
||||
@ -706,12 +707,18 @@ def render_connections(headline_id, content, graph):
|
||||
if headline_id not in graph['backlinks']:
|
||||
return
|
||||
|
||||
content.append("<ul><li class='connections'><span class='tag backlink-explanation'>Linked from</span></li><ul>")
|
||||
for backlink in sorted(graph['backlinks'][headline_id], key=lambda x: graph['nodes'][x]['title']):
|
||||
link = graph["nodes"][backlink]
|
||||
title = link["title"]
|
||||
content.append(f"<li><a class='internal backlink' href='./{backlink}.node.html'>{html.escape(title)}</a></li>")
|
||||
content.append("</ul></ul></div>")
|
||||
# if headline_id != 'aa29be89-70e7-4465-91ed-361cf0ce62f2':
|
||||
# return
|
||||
|
||||
# TODO: Cache results
|
||||
# TODO: Avoid querying graph API on script
|
||||
logging.info("Generating centered graph for {}".format(headline_id))
|
||||
import subprocess
|
||||
this_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
os.makedirs('cache', exist_ok=True)
|
||||
subprocess.check_call(['python3', os.path.join(this_dir, 'gen-centered-graph.py'), headline_id, 'cache/' + headline_id + '.svg'])
|
||||
with open('cache/' + headline_id + '.svg') as f:
|
||||
content.append("<div class='connections'>{}</div>".format(f.read()))
|
||||
|
||||
def render(headline, doc, graph, headlineLevel):
|
||||
try:
|
||||
|
@ -480,6 +480,17 @@ tr.__table-separator {
|
||||
border-bottom: 0.5ex solid black;
|
||||
}
|
||||
|
||||
.connections svg {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.connections svg #graph0 > polygon {
|
||||
/* Main box */
|
||||
fill: transparent;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
/* Side-to-side */
|
||||
@media (min-width: 120ex) {
|
||||
body:not(.no-toc) {
|
||||
@ -617,4 +628,15 @@ tr.__table-separator {
|
||||
tr.__table-separator {
|
||||
border-bottom: 0.5ex solid #eee;
|
||||
}
|
||||
|
||||
.connections svg polygon {
|
||||
stroke: white;
|
||||
fill: #222;
|
||||
}
|
||||
.connections svg text {
|
||||
fill: white;
|
||||
}
|
||||
.connections svg path {
|
||||
stroke: white;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user