codegraphcontext 0.3.2__py3-none-any.whl → 0.3.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- codegraphcontext/cli/cli_helpers.py +40 -244
- codegraphcontext/cli/main.py +1 -1
- codegraphcontext/cli/visualizer.py +38 -1379
- codegraphcontext/tools/graph_builder.py +76 -60
- codegraphcontext/tools/handlers/query_handlers.py +16 -110
- codegraphcontext/utils/visualize_graph.py +0 -150
- codegraphcontext/viz/server.py +117 -65
- {codegraphcontext-0.3.2.dist-info → codegraphcontext-0.3.4.dist-info}/METADATA +4 -2
- {codegraphcontext-0.3.2.dist-info → codegraphcontext-0.3.4.dist-info}/RECORD +13 -13
- {codegraphcontext-0.3.2.dist-info → codegraphcontext-0.3.4.dist-info}/WHEEL +0 -0
- {codegraphcontext-0.3.2.dist-info → codegraphcontext-0.3.4.dist-info}/entry_points.txt +0 -0
- {codegraphcontext-0.3.2.dist-info → codegraphcontext-0.3.4.dist-info}/licenses/LICENSE +0 -0
- {codegraphcontext-0.3.2.dist-info → codegraphcontext-0.3.4.dist-info}/top_level.txt +0 -0
|
@@ -340,7 +340,9 @@ def cypher_helper_visual(query: str):
|
|
|
340
340
|
console.print(f"[bold red]An error occurred while executing query:[/bold red] {e}")
|
|
341
341
|
finally:
|
|
342
342
|
db_manager.close_driver()
|
|
343
|
-
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
import uvicorn
|
|
344
346
|
import urllib.parse
|
|
345
347
|
from ..viz.server import run_server, set_db_manager
|
|
346
348
|
|
|
@@ -356,12 +358,42 @@ def visualize_helper(repo_path: Optional[str] = None, port: int = 8000):
|
|
|
356
358
|
set_db_manager(db_manager)
|
|
357
359
|
|
|
358
360
|
# Determine the static directory (built React app)
|
|
359
|
-
|
|
361
|
+
# This points to src/codegraphcontext/viz/dist where we build the website
|
|
362
|
+
# (relative to src/codegraphcontext/cli/cli_helpers.py)
|
|
363
|
+
# Using .resolve() is more robust for path comparison and existence checks
|
|
364
|
+
this_file = Path(__file__).resolve()
|
|
365
|
+
package_root = this_file.parent.parent
|
|
366
|
+
static_dir = package_root / "viz" / "dist"
|
|
367
|
+
|
|
368
|
+
# Fallback for development if not yet built in viz/dist
|
|
360
369
|
if not static_dir.exists():
|
|
361
|
-
|
|
362
|
-
#
|
|
363
|
-
|
|
364
|
-
|
|
370
|
+
# Look for website/dist in the project root (3 levels up from cli/cli_helpers.py, 4 parents)
|
|
371
|
+
# 1: cli/, 2: codegraphcontext/, 3: src/, 4: project_root/
|
|
372
|
+
project_root = this_file.parent.parent.parent.parent
|
|
373
|
+
dev_static_dir = project_root / "website" / "dist"
|
|
374
|
+
|
|
375
|
+
# Also try one level up from package_root just in case of different layouts
|
|
376
|
+
alt_dev_dir = package_root.parent.parent / "website" / "dist"
|
|
377
|
+
|
|
378
|
+
if dev_static_dir.exists():
|
|
379
|
+
static_dir = dev_static_dir
|
|
380
|
+
elif alt_dev_dir.exists():
|
|
381
|
+
static_dir = alt_dev_dir
|
|
382
|
+
else:
|
|
383
|
+
# Last resort: try current working directory
|
|
384
|
+
cwd_static_dir = Path.cwd() / "website" / "dist"
|
|
385
|
+
if cwd_static_dir.exists():
|
|
386
|
+
static_dir = cwd_static_dir
|
|
387
|
+
else:
|
|
388
|
+
console.print(f"[yellow]Warning: Visualization assets not found.[/yellow]")
|
|
389
|
+
console.print(f"[dim]Checked paths:[/dim]")
|
|
390
|
+
console.print(f" [dim]- {static_dir}[/dim]")
|
|
391
|
+
console.print(f" [dim]- {dev_static_dir}[/dim]")
|
|
392
|
+
console.print(f" [dim]- {alt_dev_dir}[/dim]")
|
|
393
|
+
console.print(f" [dim]- {cwd_static_dir}[/dim]")
|
|
394
|
+
console.print("[dim]Please run 'cd website && npm run build' first.[/dim]")
|
|
395
|
+
# We continue anyway to let the server start (helpful for dev)
|
|
396
|
+
|
|
365
397
|
# Construct the URL
|
|
366
398
|
backend_url = f"http://localhost:{port}"
|
|
367
399
|
params = {"backend": backend_url}
|
|
@@ -369,7 +401,7 @@ def visualize_helper(repo_path: Optional[str] = None, port: int = 8000):
|
|
|
369
401
|
params["repo_path"] = str(Path(repo_path).resolve())
|
|
370
402
|
|
|
371
403
|
query_string = urllib.parse.urlencode(params)
|
|
372
|
-
visualization_url = f"{backend_url}/
|
|
404
|
+
visualization_url = f"{backend_url}/explore?{query_string}"
|
|
373
405
|
|
|
374
406
|
console.print(f"[green]Starting visualizer server on {backend_url}...[/green]")
|
|
375
407
|
console.print(f"[cyan]Opening Playground UI:[/cyan] {visualization_url}")
|
|
@@ -377,6 +409,7 @@ def visualize_helper(repo_path: Optional[str] = None, port: int = 8000):
|
|
|
377
409
|
# Open browser in a separate thread/process if possible, or just before starting server
|
|
378
410
|
def open_browser():
|
|
379
411
|
import time
|
|
412
|
+
import webbrowser
|
|
380
413
|
time.sleep(1.5) # Give the server a moment to start
|
|
381
414
|
webbrowser.open(visualization_url)
|
|
382
415
|
|
|
@@ -390,243 +423,6 @@ def visualize_helper(repo_path: Optional[str] = None, port: int = 8000):
|
|
|
390
423
|
finally:
|
|
391
424
|
db_manager.close_driver()
|
|
392
425
|
|
|
393
|
-
def _visualize_falkordb(db_manager):
|
|
394
|
-
console.print("[dim]Generating FalkorDB visualization (showing up to 500 relationships)...[/dim]")
|
|
395
|
-
try:
|
|
396
|
-
data_nodes = []
|
|
397
|
-
data_edges = []
|
|
398
|
-
|
|
399
|
-
with db_manager.get_driver().session() as session:
|
|
400
|
-
# Fetch nodes and edges
|
|
401
|
-
q = "MATCH (n)-[r]->(m) RETURN n, r, m LIMIT 500"
|
|
402
|
-
result = session.run(q)
|
|
403
|
-
|
|
404
|
-
seen_nodes = set()
|
|
405
|
-
|
|
406
|
-
for record in result:
|
|
407
|
-
# record values are Node/Relationship objects from falkordb client
|
|
408
|
-
n = record['n']
|
|
409
|
-
r = record['r']
|
|
410
|
-
m = record['m']
|
|
411
|
-
|
|
412
|
-
# Process Node helper
|
|
413
|
-
def process_node(node):
|
|
414
|
-
nid = getattr(node, 'id', -1)
|
|
415
|
-
labels = getattr(node, 'labels', [])
|
|
416
|
-
lbl = list(labels)[0] if labels else "Node"
|
|
417
|
-
props = getattr(node, 'properties', {})
|
|
418
|
-
name = props.get('name', str(nid))
|
|
419
|
-
|
|
420
|
-
if nid not in seen_nodes:
|
|
421
|
-
seen_nodes.add(nid)
|
|
422
|
-
color = "#97c2fc" # Default blue
|
|
423
|
-
if "Repository" in labels: color = "#ffb3ba" # Red
|
|
424
|
-
elif "File" in labels: color = "#baffc9" # Green
|
|
425
|
-
elif "Class" in labels: color = "#bae1ff" # Light Blue
|
|
426
|
-
elif "Function" in labels: color = "#ffffba" # Yellow
|
|
427
|
-
elif "Package" in labels: color = "#ffdfba" # Orange
|
|
428
|
-
|
|
429
|
-
data_nodes.append({
|
|
430
|
-
"id": nid,
|
|
431
|
-
"label": name,
|
|
432
|
-
"group": lbl,
|
|
433
|
-
"title": str(props),
|
|
434
|
-
"color": color
|
|
435
|
-
})
|
|
436
|
-
return nid
|
|
437
|
-
|
|
438
|
-
nid = process_node(n)
|
|
439
|
-
mid = process_node(m)
|
|
440
|
-
|
|
441
|
-
# Check Edge
|
|
442
|
-
e_type = getattr(r, 'relation', '') or getattr(r, 'type', 'REL')
|
|
443
|
-
data_edges.append({
|
|
444
|
-
"from": nid,
|
|
445
|
-
"to": mid,
|
|
446
|
-
"label": e_type,
|
|
447
|
-
"arrows": "to"
|
|
448
|
-
})
|
|
449
|
-
|
|
450
|
-
filename = "codegraph_viz.html"
|
|
451
|
-
html_content = f"""
|
|
452
|
-
<!DOCTYPE html>
|
|
453
|
-
<html>
|
|
454
|
-
<head>
|
|
455
|
-
<title>CodeGraphContext Visualization</title>
|
|
456
|
-
<script type="text/javascript" src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script>
|
|
457
|
-
<style type="text/css">
|
|
458
|
-
#mynetwork {{
|
|
459
|
-
width: 100%;
|
|
460
|
-
height: 100vh;
|
|
461
|
-
border: 1px solid lightgray;
|
|
462
|
-
}}
|
|
463
|
-
</style>
|
|
464
|
-
</head>
|
|
465
|
-
<body>
|
|
466
|
-
<div id="mynetwork"></div>
|
|
467
|
-
<script type="text/javascript">
|
|
468
|
-
var nodes = new vis.DataSet({json.dumps(data_nodes)});
|
|
469
|
-
var edges = new vis.DataSet({json.dumps(data_edges)});
|
|
470
|
-
var container = document.getElementById('mynetwork');
|
|
471
|
-
var data = {{ nodes: nodes, edges: edges }};
|
|
472
|
-
var options = {{
|
|
473
|
-
nodes: {{ shape: 'dot', size: 16 }},
|
|
474
|
-
physics: {{ stabilization: false }},
|
|
475
|
-
layout: {{ improvedLayout: false }}
|
|
476
|
-
}};
|
|
477
|
-
var network = new vis.Network(container, data, options);
|
|
478
|
-
</script>
|
|
479
|
-
</body>
|
|
480
|
-
</html>
|
|
481
|
-
"""
|
|
482
|
-
|
|
483
|
-
out_path = Path(filename).resolve()
|
|
484
|
-
with open(out_path, "w") as f:
|
|
485
|
-
f.write(html_content)
|
|
486
|
-
|
|
487
|
-
console.print(f"[green]Visualization generated at:[/green] {out_path}")
|
|
488
|
-
console.print("Opening in default browser...")
|
|
489
|
-
webbrowser.open(f"file://{out_path}")
|
|
490
|
-
|
|
491
|
-
except Exception as e:
|
|
492
|
-
console.print(f"[bold red]Visualization failed:[/bold red] {e}")
|
|
493
|
-
import traceback
|
|
494
|
-
traceback.print_exc()
|
|
495
|
-
finally:
|
|
496
|
-
db_manager.close_driver()
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
def _visualize_kuzudb(db_manager):
|
|
500
|
-
console.print("[dim]Generating KùzuDB visualization (showing up to 500 relationships)...[/dim]")
|
|
501
|
-
try:
|
|
502
|
-
data_nodes = []
|
|
503
|
-
data_edges = []
|
|
504
|
-
|
|
505
|
-
with db_manager.get_driver().session() as session:
|
|
506
|
-
# Fetch nodes and edges
|
|
507
|
-
# KùzuDB returns dicts for n, r, m in the result
|
|
508
|
-
q = "MATCH (n)-[r]->(m) RETURN n, r, m LIMIT 500"
|
|
509
|
-
result = session.run(q)
|
|
510
|
-
|
|
511
|
-
seen_nodes = set()
|
|
512
|
-
|
|
513
|
-
# Helper to extract Node ID and props
|
|
514
|
-
def process_node(node):
|
|
515
|
-
uid = None
|
|
516
|
-
lbl = 'Node'
|
|
517
|
-
props = {}
|
|
518
|
-
|
|
519
|
-
# Handle Kuzu Node Object (processed by wrapper)
|
|
520
|
-
if hasattr(node, 'properties'):
|
|
521
|
-
props = node.properties or {}
|
|
522
|
-
if hasattr(node, 'labels') and node.labels:
|
|
523
|
-
lbl = node.labels[0]
|
|
524
|
-
if hasattr(node, 'id'):
|
|
525
|
-
uid = str(node.id)
|
|
526
|
-
# Handle Dictionary (raw Kuzu result)
|
|
527
|
-
elif isinstance(node, dict):
|
|
528
|
-
if '_id' in node:
|
|
529
|
-
uid = f"{node['_id']['table']}_{node['_id']['offset']}"
|
|
530
|
-
lbl = node.get('_label', 'Node')
|
|
531
|
-
props = {k: v for k, v in node.items() if not k.startswith('_')}
|
|
532
|
-
|
|
533
|
-
if not uid:
|
|
534
|
-
uid = str(uuid.uuid4())
|
|
535
|
-
|
|
536
|
-
name = props.get('name', str(uid))
|
|
537
|
-
|
|
538
|
-
if uid not in seen_nodes:
|
|
539
|
-
seen_nodes.add(uid)
|
|
540
|
-
color = "#97c2fc" # Default blue
|
|
541
|
-
if "Repository" == lbl: color = "#ffb3ba"
|
|
542
|
-
elif "File" == lbl: color = "#baffc9"
|
|
543
|
-
elif "Class" == lbl: color = "#bae1ff"
|
|
544
|
-
elif "Function" == lbl: color = "#ffffba"
|
|
545
|
-
elif "Module" == lbl: color = "#ffdfba"
|
|
546
|
-
|
|
547
|
-
data_nodes.append({
|
|
548
|
-
"id": uid,
|
|
549
|
-
"label": name,
|
|
550
|
-
"group": lbl,
|
|
551
|
-
"title": str(props),
|
|
552
|
-
"color": color
|
|
553
|
-
})
|
|
554
|
-
return uid
|
|
555
|
-
|
|
556
|
-
# Iterate results
|
|
557
|
-
for record in result:
|
|
558
|
-
# record is dict-like access to row items
|
|
559
|
-
n = record['n']
|
|
560
|
-
r = record['r']
|
|
561
|
-
m = record['m']
|
|
562
|
-
|
|
563
|
-
nid = process_node(n)
|
|
564
|
-
mid = process_node(m)
|
|
565
|
-
|
|
566
|
-
# Process Edge
|
|
567
|
-
e_type = 'REL'
|
|
568
|
-
if hasattr(r, 'type'):
|
|
569
|
-
e_type = r.type
|
|
570
|
-
elif isinstance(r, dict):
|
|
571
|
-
e_type = r.get('_label', 'REL')
|
|
572
|
-
elif hasattr(r, 'label'): # Some versions
|
|
573
|
-
e_type = r.label
|
|
574
|
-
|
|
575
|
-
data_edges.append({
|
|
576
|
-
"from": nid,
|
|
577
|
-
"to": mid,
|
|
578
|
-
"label": e_type,
|
|
579
|
-
"arrows": "to"
|
|
580
|
-
})
|
|
581
|
-
|
|
582
|
-
filename = "codegraph_viz.html"
|
|
583
|
-
html_content = f"""
|
|
584
|
-
<!DOCTYPE html>
|
|
585
|
-
<html>
|
|
586
|
-
<head>
|
|
587
|
-
<title>CodeGraphContext KùzuDB Visualization</title>
|
|
588
|
-
<script type="text/javascript" src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script>
|
|
589
|
-
<style type="text/css">
|
|
590
|
-
#mynetwork {{
|
|
591
|
-
width: 100%;
|
|
592
|
-
height: 100vh;
|
|
593
|
-
border: 1px solid lightgray;
|
|
594
|
-
}}
|
|
595
|
-
</style>
|
|
596
|
-
</head>
|
|
597
|
-
<body>
|
|
598
|
-
<div id="mynetwork"></div>
|
|
599
|
-
<script type="text/javascript">
|
|
600
|
-
var nodes = new vis.DataSet({json.dumps(data_nodes)});
|
|
601
|
-
var edges = new vis.DataSet({json.dumps(data_edges)});
|
|
602
|
-
var container = document.getElementById('mynetwork');
|
|
603
|
-
var data = {{ nodes: nodes, edges: edges }};
|
|
604
|
-
var options = {{
|
|
605
|
-
nodes: {{ shape: 'dot', size: 16 }},
|
|
606
|
-
physics: {{ stabilization: false }},
|
|
607
|
-
layout: {{ improvedLayout: false }}
|
|
608
|
-
}};
|
|
609
|
-
var network = new vis.Network(container, data, options);
|
|
610
|
-
</script>
|
|
611
|
-
</body>
|
|
612
|
-
</html>
|
|
613
|
-
"""
|
|
614
|
-
|
|
615
|
-
out_path = Path(filename).resolve()
|
|
616
|
-
with open(out_path, "w") as f:
|
|
617
|
-
f.write(html_content)
|
|
618
|
-
|
|
619
|
-
console.print(f"[green]Visualization generated at:[/green] {out_path}")
|
|
620
|
-
console.print("Opening in default browser...")
|
|
621
|
-
webbrowser.open(f"file://{out_path}")
|
|
622
|
-
|
|
623
|
-
except Exception as e:
|
|
624
|
-
console.print(f"[bold red]Visualization failed:[/bold red] {e}")
|
|
625
|
-
import traceback
|
|
626
|
-
traceback.print_exc()
|
|
627
|
-
finally:
|
|
628
|
-
db_manager.close_driver()
|
|
629
|
-
|
|
630
426
|
|
|
631
427
|
def reindex_helper(path: str):
|
|
632
428
|
"""Force re-index by deleting and rebuilding the repository."""
|
codegraphcontext/cli/main.py
CHANGED
|
@@ -999,7 +999,7 @@ def visualize(
|
|
|
999
999
|
port: int = typer.Option(8000, "--port", "-p", help="Port to run the visualizer server on.")
|
|
1000
1000
|
):
|
|
1001
1001
|
"""
|
|
1002
|
-
Launches the interactive
|
|
1002
|
+
Launches the interactive UI to visualize the code graph.
|
|
1003
1003
|
"""
|
|
1004
1004
|
_load_credentials()
|
|
1005
1005
|
visualize_helper(repo, port)
|