sql-glider 0.1.24__py3-none-any.whl → 0.1.26__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.
- {sql_glider-0.1.24.dist-info → sql_glider-0.1.26.dist-info}/METADATA +1 -1
- {sql_glider-0.1.24.dist-info → sql_glider-0.1.26.dist-info}/RECORD +8 -8
- sqlglider/_version.py +2 -2
- sqlglider/cli.py +7 -3
- sqlglider/graph/diagram_formatters.py +141 -35
- {sql_glider-0.1.24.dist-info → sql_glider-0.1.26.dist-info}/WHEEL +0 -0
- {sql_glider-0.1.24.dist-info → sql_glider-0.1.26.dist-info}/entry_points.txt +0 -0
- {sql_glider-0.1.24.dist-info → sql_glider-0.1.26.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sql-glider
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.26
|
|
4
4
|
Summary: SQL Utility Toolkit for better understanding, use, and governance of your queries in a native environment.
|
|
5
5
|
Project-URL: Homepage, https://github.com/rycowhi/sql-glider/
|
|
6
6
|
Project-URL: Repository, https://github.com/rycowhi/sql-glider/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
sqlglider/__init__.py,sha256=gDf7s52dMcX7JuCZ1SLawcB1vb3U0yJCohu9RQAATBY,125
|
|
2
|
-
sqlglider/_version.py,sha256=
|
|
3
|
-
sqlglider/cli.py,sha256=
|
|
2
|
+
sqlglider/_version.py,sha256=Y9o7KiJWiG6n9XbSpMICgNgajFRbL4an-gN1BQc-jwM,706
|
|
3
|
+
sqlglider/cli.py,sha256=If-USLXwbTcP_ehacyPcahD5aygmkqI9ycxJDqIpalg,69701
|
|
4
4
|
sqlglider/global_models.py,sha256=2vyJXAuXOsXQpE-D3F0ejj7eR9z0nDWFjTkielhzM8k,356
|
|
5
5
|
sqlglider/catalog/__init__.py,sha256=2PqFPyzFXJ14FpSUcBmVK2L-a_ypWQHAbHFHxLDk_LE,814
|
|
6
6
|
sqlglider/catalog/base.py,sha256=R7htHC43InpH4uRjYk33dMYYji6oylHns7Ye_mgfjJE,3116
|
|
@@ -12,7 +12,7 @@ sqlglider/dissection/formatters.py,sha256=M7gsmTNljRIeLIRv4D0vHvqJVrTqWSpsg7vem8
|
|
|
12
12
|
sqlglider/dissection/models.py,sha256=RRD3RIteqbUBY6e-74skKDvMH3qeAUaqA2sFcrjP5GQ,3618
|
|
13
13
|
sqlglider/graph/__init__.py,sha256=4DDdrPM75CmeQWt7wHdBsjCm1s70BHGLYdijIbaUEKY,871
|
|
14
14
|
sqlglider/graph/builder.py,sha256=VNBdsDlkiaId3JGvr2G4h6OIFek_9zPsGMIYL9GpJlk,15796
|
|
15
|
-
sqlglider/graph/diagram_formatters.py,sha256=
|
|
15
|
+
sqlglider/graph/diagram_formatters.py,sha256=S8K0pjSzRTcwT1yi7QJUP1gdfPR2p3Om93bVdtJZuxA,26465
|
|
16
16
|
sqlglider/graph/formatters.py,sha256=p85-WN9oPmEETsAtWSo1sIQELF36w85QoFEJyfBZGoM,4800
|
|
17
17
|
sqlglider/graph/merge.py,sha256=uUZlm4BN3S9gRL66Cc2mzhbtuh4SVAv2n4cN4eUEQBU,4077
|
|
18
18
|
sqlglider/graph/models.py,sha256=EYmjv_WzDSNp_WfhJ6H-qBIOkAcoNKS7GRUryfKrHuY,9330
|
|
@@ -32,8 +32,8 @@ sqlglider/utils/__init__.py,sha256=KGp9-UzKz_OFBOTFoSy-g-NXDZsvyWXG_9-1zcC6ePE,2
|
|
|
32
32
|
sqlglider/utils/config.py,sha256=qx5zE9pjLCCzHQDFVPLVd7LgJ-lghxUa2x-aZOAHByY,4962
|
|
33
33
|
sqlglider/utils/file_utils.py,sha256=5_ff28E0r1R7emZzsOnRuHd-7zIX6873eyr1SuPEr4E,1093
|
|
34
34
|
sqlglider/utils/schema.py,sha256=LiWrYDunXKJdoSlpKmIaIQ2hLSaIN1iQHqkXjMpGzRE,1883
|
|
35
|
-
sql_glider-0.1.
|
|
36
|
-
sql_glider-0.1.
|
|
37
|
-
sql_glider-0.1.
|
|
38
|
-
sql_glider-0.1.
|
|
39
|
-
sql_glider-0.1.
|
|
35
|
+
sql_glider-0.1.26.dist-info/METADATA,sha256=xgItNGWc-KIxIsKFKj-sC6Dl1rCPuMuEyzk7XeDi2a4,31197
|
|
36
|
+
sql_glider-0.1.26.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
37
|
+
sql_glider-0.1.26.dist-info/entry_points.txt,sha256=HDuakHqHS5C0HFKsMIxMYmDU7-BLBGrnIJcYaVRu-s0,251
|
|
38
|
+
sql_glider-0.1.26.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
39
|
+
sql_glider-0.1.26.dist-info/RECORD,,
|
sqlglider/_version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.1.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 1,
|
|
31
|
+
__version__ = version = '0.1.26'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 1, 26)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
sqlglider/cli.py
CHANGED
|
@@ -1715,11 +1715,15 @@ def graph_query(
|
|
|
1715
1715
|
elif output_format == "mermaid":
|
|
1716
1716
|
from sqlglider.graph.diagram_formatters import MermaidFormatter
|
|
1717
1717
|
|
|
1718
|
-
print(MermaidFormatter.format_query_result(result))
|
|
1718
|
+
print(MermaidFormatter.format_query_result(result, graph=querier.graph))
|
|
1719
1719
|
elif output_format == "mermaid-markdown":
|
|
1720
1720
|
from sqlglider.graph.diagram_formatters import MermaidMarkdownFormatter
|
|
1721
1721
|
|
|
1722
|
-
print(
|
|
1722
|
+
print(
|
|
1723
|
+
MermaidMarkdownFormatter.format_query_result(
|
|
1724
|
+
result, graph=querier.graph
|
|
1725
|
+
)
|
|
1726
|
+
)
|
|
1723
1727
|
elif output_format == "dot":
|
|
1724
1728
|
from sqlglider.graph.diagram_formatters import DotFormatter
|
|
1725
1729
|
|
|
@@ -1727,7 +1731,7 @@ def graph_query(
|
|
|
1727
1731
|
elif output_format == "plotly":
|
|
1728
1732
|
from sqlglider.graph.diagram_formatters import PlotlyFormatter
|
|
1729
1733
|
|
|
1730
|
-
print(PlotlyFormatter.format_query_result(result))
|
|
1734
|
+
print(PlotlyFormatter.format_query_result(result, graph=querier.graph))
|
|
1731
1735
|
|
|
1732
1736
|
except ImportError as e:
|
|
1733
1737
|
err_console.print(f"[red]Error:[/red] {e}")
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import json
|
|
4
4
|
import re
|
|
5
5
|
from collections import defaultdict
|
|
6
|
-
from typing import Any, Set
|
|
6
|
+
from typing import Any, Optional, Set
|
|
7
7
|
|
|
8
8
|
from sqlglider.graph.models import LineageGraph
|
|
9
9
|
from sqlglider.graph.query import LineageQueryResult
|
|
@@ -102,16 +102,26 @@ class MermaidFormatter:
|
|
|
102
102
|
node_id = _sanitize_mermaid_id(node.identifier)
|
|
103
103
|
lines.append(f' {node_id}["{node.identifier}"]')
|
|
104
104
|
|
|
105
|
-
# Add edges
|
|
105
|
+
# Add edges with file path labels
|
|
106
106
|
for edge in graph.edges:
|
|
107
107
|
src = _sanitize_mermaid_id(edge.source_node)
|
|
108
108
|
tgt = _sanitize_mermaid_id(edge.target_node)
|
|
109
|
-
|
|
109
|
+
# Extract filename from path
|
|
110
|
+
file_name = (
|
|
111
|
+
edge.file_path.split("/")[-1].split("\\")[-1] if edge.file_path else ""
|
|
112
|
+
)
|
|
113
|
+
if file_name:
|
|
114
|
+
lines.append(f" {src} -->|{file_name}| {tgt}")
|
|
115
|
+
else:
|
|
116
|
+
lines.append(f" {src} --> {tgt}")
|
|
110
117
|
|
|
111
118
|
return "\n".join(lines)
|
|
112
119
|
|
|
113
120
|
@staticmethod
|
|
114
|
-
def format_query_result(
|
|
121
|
+
def format_query_result(
|
|
122
|
+
result: LineageQueryResult,
|
|
123
|
+
graph: Optional[LineageGraph] = None,
|
|
124
|
+
) -> str:
|
|
115
125
|
"""Format query result as a Mermaid flowchart with styling.
|
|
116
126
|
|
|
117
127
|
The queried column is highlighted in amber, root nodes in teal,
|
|
@@ -119,6 +129,7 @@ class MermaidFormatter:
|
|
|
119
129
|
|
|
120
130
|
Args:
|
|
121
131
|
result: LineageQueryResult from upstream/downstream query
|
|
132
|
+
graph: Optional LineageGraph for edge file path labels
|
|
122
133
|
|
|
123
134
|
Returns:
|
|
124
135
|
Mermaid diagram string with style directives and legend
|
|
@@ -134,16 +145,27 @@ class MermaidFormatter:
|
|
|
134
145
|
all_nodes = _collect_query_nodes(result)
|
|
135
146
|
edges = _collect_query_edges(result)
|
|
136
147
|
|
|
148
|
+
# Build edge file path lookup if graph is provided
|
|
149
|
+
edge_file_paths: dict[tuple[str, str], str] = {}
|
|
150
|
+
if graph:
|
|
151
|
+
for e in graph.edges:
|
|
152
|
+
edge_file_paths[(e.source_node, e.target_node)] = e.file_path
|
|
153
|
+
|
|
137
154
|
# Declare nodes
|
|
138
155
|
for identifier in sorted(all_nodes):
|
|
139
156
|
node_id = _sanitize_mermaid_id(identifier)
|
|
140
157
|
lines.append(f' {node_id}["{identifier}"]')
|
|
141
158
|
|
|
142
|
-
# Add edges
|
|
159
|
+
# Add edges with optional file path labels
|
|
143
160
|
for src, tgt in sorted(edges):
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
)
|
|
161
|
+
src_id = _sanitize_mermaid_id(src)
|
|
162
|
+
tgt_id = _sanitize_mermaid_id(tgt)
|
|
163
|
+
file_path = edge_file_paths.get((src, tgt), "")
|
|
164
|
+
file_name = file_path.split("/")[-1].split("\\")[-1] if file_path else ""
|
|
165
|
+
if file_name:
|
|
166
|
+
lines.append(f" {src_id} -->|{file_name}| {tgt_id}")
|
|
167
|
+
else:
|
|
168
|
+
lines.append(f" {src_id} --> {tgt_id}")
|
|
147
169
|
|
|
148
170
|
# Style directives
|
|
149
171
|
queried_id = _sanitize_mermaid_id(result.query_column)
|
|
@@ -200,16 +222,20 @@ class MermaidMarkdownFormatter:
|
|
|
200
222
|
return f"```mermaid\n{mermaid}\n```"
|
|
201
223
|
|
|
202
224
|
@staticmethod
|
|
203
|
-
def format_query_result(
|
|
225
|
+
def format_query_result(
|
|
226
|
+
result: LineageQueryResult,
|
|
227
|
+
graph: Optional[LineageGraph] = None,
|
|
228
|
+
) -> str:
|
|
204
229
|
"""Format query result as a Mermaid diagram in a markdown code block.
|
|
205
230
|
|
|
206
231
|
Args:
|
|
207
232
|
result: LineageQueryResult from upstream/downstream query
|
|
233
|
+
graph: Optional LineageGraph for edge file path labels
|
|
208
234
|
|
|
209
235
|
Returns:
|
|
210
236
|
Markdown string with fenced Mermaid diagram
|
|
211
237
|
"""
|
|
212
|
-
mermaid = MermaidFormatter.format_query_result(result)
|
|
238
|
+
mermaid = MermaidFormatter.format_query_result(result, graph=graph)
|
|
213
239
|
return f"```mermaid\n{mermaid}\n```"
|
|
214
240
|
|
|
215
241
|
|
|
@@ -335,6 +361,8 @@ class DotFormatter:
|
|
|
335
361
|
def _compute_layered_layout(
|
|
336
362
|
nodes: list[str],
|
|
337
363
|
edges: list[tuple[str, str]],
|
|
364
|
+
x_spacing: float = 250.0,
|
|
365
|
+
y_spacing: float = 100.0,
|
|
338
366
|
) -> dict[str, tuple[float, float]]:
|
|
339
367
|
"""Compute layered layout positions for nodes using topological ordering.
|
|
340
368
|
|
|
@@ -345,6 +373,8 @@ def _compute_layered_layout(
|
|
|
345
373
|
Args:
|
|
346
374
|
nodes: List of node identifiers
|
|
347
375
|
edges: List of (source, target) edge tuples
|
|
376
|
+
x_spacing: Horizontal spacing between layers (default: 250 pixels)
|
|
377
|
+
y_spacing: Vertical spacing between nodes in the same layer (default: 100 pixels)
|
|
348
378
|
|
|
349
379
|
Returns:
|
|
350
380
|
Dictionary mapping node identifiers to (x, y) positions
|
|
@@ -395,15 +425,13 @@ def _compute_layered_layout(
|
|
|
395
425
|
|
|
396
426
|
# Compute positions: x based on layer, y spread vertically within layer
|
|
397
427
|
positions: dict[str, tuple[float, float]] = {}
|
|
398
|
-
max_layer = max(layers.values()) if layers else 0
|
|
399
|
-
x_spacing = 1.0 if max_layer == 0 else 1.0
|
|
400
428
|
|
|
401
429
|
for layer, layer_nodes in layer_groups.items():
|
|
402
430
|
x = layer * x_spacing
|
|
403
431
|
n = len(layer_nodes)
|
|
404
432
|
for i, node in enumerate(sorted(layer_nodes)):
|
|
405
|
-
# Center nodes vertically, spread them out
|
|
406
|
-
y = (i - (n - 1) / 2) *
|
|
433
|
+
# Center nodes vertically, spread them out uniformly
|
|
434
|
+
y = (i - (n - 1) / 2) * y_spacing
|
|
407
435
|
positions[node] = (x, y)
|
|
408
436
|
|
|
409
437
|
return positions
|
|
@@ -444,6 +472,11 @@ class PlotlyFormatter:
|
|
|
444
472
|
node_ids = [n.identifier for n in graph.nodes]
|
|
445
473
|
edge_tuples = [(e.source_node, e.target_node) for e in graph.edges]
|
|
446
474
|
|
|
475
|
+
# Build lookup for edge file paths
|
|
476
|
+
edge_file_paths: dict[tuple[str, str], str] = {}
|
|
477
|
+
for e in graph.edges:
|
|
478
|
+
edge_file_paths[(e.source_node, e.target_node)] = e.file_path
|
|
479
|
+
|
|
447
480
|
if not node_ids:
|
|
448
481
|
# Empty graph
|
|
449
482
|
figure: dict[str, Any] = {
|
|
@@ -459,24 +492,46 @@ class PlotlyFormatter:
|
|
|
459
492
|
|
|
460
493
|
positions = _compute_layered_layout(node_ids, edge_tuples)
|
|
461
494
|
|
|
462
|
-
# Build edge traces
|
|
495
|
+
# Build edge traces with hover text for file path
|
|
463
496
|
edge_traces: list[dict[str, Any]] = []
|
|
497
|
+
edge_annotations: list[dict[str, Any]] = []
|
|
464
498
|
for src, tgt in edge_tuples:
|
|
465
499
|
if src in positions and tgt in positions:
|
|
466
500
|
x0, y0 = positions[src]
|
|
467
501
|
x1, y1 = positions[tgt]
|
|
502
|
+
file_path = edge_file_paths.get((src, tgt), "")
|
|
503
|
+
# Extract just the filename for display
|
|
504
|
+
file_name = (
|
|
505
|
+
file_path.split("/")[-1].split("\\")[-1] if file_path else ""
|
|
506
|
+
)
|
|
507
|
+
|
|
468
508
|
edge_traces.append(
|
|
469
509
|
{
|
|
470
510
|
"type": "scatter",
|
|
471
511
|
"x": [x0, x1, None],
|
|
472
512
|
"y": [y0, y1, None],
|
|
473
513
|
"mode": "lines",
|
|
474
|
-
"line": {"width": 1, "color": "#888"},
|
|
475
|
-
"hoverinfo": "
|
|
514
|
+
"line": {"width": 1.5, "color": "#888"},
|
|
515
|
+
"hoverinfo": "text",
|
|
516
|
+
"hovertext": file_path,
|
|
476
517
|
"showlegend": False,
|
|
477
518
|
}
|
|
478
519
|
)
|
|
479
520
|
|
|
521
|
+
# Add annotation at midpoint of edge
|
|
522
|
+
mid_x = (x0 + x1) / 2
|
|
523
|
+
mid_y = (y0 + y1) / 2
|
|
524
|
+
edge_annotations.append(
|
|
525
|
+
{
|
|
526
|
+
"x": mid_x,
|
|
527
|
+
"y": mid_y,
|
|
528
|
+
"text": file_name,
|
|
529
|
+
"showarrow": False,
|
|
530
|
+
"font": {"size": 9, "color": "#666"},
|
|
531
|
+
"bgcolor": "rgba(255,255,255,0.8)",
|
|
532
|
+
}
|
|
533
|
+
)
|
|
534
|
+
|
|
480
535
|
# Build node trace
|
|
481
536
|
node_x = [positions[n][0] for n in node_ids if n in positions]
|
|
482
537
|
node_y = [positions[n][1] for n in node_ids if n in positions]
|
|
@@ -489,15 +544,21 @@ class PlotlyFormatter:
|
|
|
489
544
|
"mode": "markers+text",
|
|
490
545
|
"text": node_text,
|
|
491
546
|
"textposition": "top center",
|
|
547
|
+
"textfont": {"size": 11},
|
|
492
548
|
"hoverinfo": "text",
|
|
493
549
|
"marker": {
|
|
494
|
-
"size":
|
|
550
|
+
"size": 15,
|
|
495
551
|
"color": "#6495ED",
|
|
496
552
|
"line": {"width": 2, "color": "#4169E1"},
|
|
497
553
|
},
|
|
498
554
|
"showlegend": False,
|
|
499
555
|
}
|
|
500
556
|
|
|
557
|
+
# Calculate figure dimensions based on graph size
|
|
558
|
+
min_height = 400
|
|
559
|
+
height_per_node = 50
|
|
560
|
+
calculated_height = max(min_height, len(node_ids) * height_per_node)
|
|
561
|
+
|
|
501
562
|
figure = {
|
|
502
563
|
"data": edge_traces + [node_trace],
|
|
503
564
|
"layout": {
|
|
@@ -506,14 +567,19 @@ class PlotlyFormatter:
|
|
|
506
567
|
"hovermode": "closest",
|
|
507
568
|
"xaxis": {"visible": False},
|
|
508
569
|
"yaxis": {"visible": False},
|
|
509
|
-
"
|
|
570
|
+
"height": calculated_height,
|
|
571
|
+
"margin": {"l": 50, "r": 50, "t": 60, "b": 40},
|
|
572
|
+
"annotations": edge_annotations,
|
|
510
573
|
},
|
|
511
574
|
}
|
|
512
575
|
|
|
513
576
|
return json.dumps(figure, indent=2)
|
|
514
577
|
|
|
515
578
|
@staticmethod
|
|
516
|
-
def format_query_result(
|
|
579
|
+
def format_query_result(
|
|
580
|
+
result: LineageQueryResult,
|
|
581
|
+
graph: Optional[LineageGraph] = None,
|
|
582
|
+
) -> str:
|
|
517
583
|
"""Format query result as a Plotly JSON figure with styling.
|
|
518
584
|
|
|
519
585
|
The queried column is highlighted in amber, root nodes in teal,
|
|
@@ -521,6 +587,7 @@ class PlotlyFormatter:
|
|
|
521
587
|
|
|
522
588
|
Args:
|
|
523
589
|
result: LineageQueryResult from upstream/downstream query
|
|
590
|
+
graph: Optional LineageGraph for edge file path labels
|
|
524
591
|
|
|
525
592
|
Returns:
|
|
526
593
|
JSON string representing a Plotly figure specification
|
|
@@ -530,6 +597,12 @@ class PlotlyFormatter:
|
|
|
530
597
|
all_nodes = _collect_query_nodes(result)
|
|
531
598
|
edges = _collect_query_edges(result)
|
|
532
599
|
|
|
600
|
+
# Build edge file path lookup if graph is provided
|
|
601
|
+
edge_file_paths: dict[tuple[str, str], str] = {}
|
|
602
|
+
if graph:
|
|
603
|
+
for e in graph.edges:
|
|
604
|
+
edge_file_paths[(e.source_node, e.target_node)] = e.file_path
|
|
605
|
+
|
|
533
606
|
if not all_nodes:
|
|
534
607
|
# Should not happen, but handle gracefully
|
|
535
608
|
figure: dict[str, Any] = {
|
|
@@ -568,12 +641,18 @@ class PlotlyFormatter:
|
|
|
568
641
|
else:
|
|
569
642
|
node_colors.append("#6495ED") # Default blue
|
|
570
643
|
|
|
571
|
-
# Build edge traces
|
|
644
|
+
# Build edge traces with optional file path labels
|
|
572
645
|
edge_traces: list[dict[str, Any]] = []
|
|
646
|
+
edge_annotations: list[dict[str, Any]] = []
|
|
573
647
|
for src, tgt in sorted(edges):
|
|
574
648
|
if src in positions and tgt in positions:
|
|
575
649
|
x0, y0 = positions[src]
|
|
576
650
|
x1, y1 = positions[tgt]
|
|
651
|
+
file_path = edge_file_paths.get((src, tgt), "")
|
|
652
|
+
file_name = (
|
|
653
|
+
file_path.split("/")[-1].split("\\")[-1] if file_path else ""
|
|
654
|
+
)
|
|
655
|
+
|
|
577
656
|
edge_traces.append(
|
|
578
657
|
{
|
|
579
658
|
"type": "scatter",
|
|
@@ -581,11 +660,27 @@ class PlotlyFormatter:
|
|
|
581
660
|
"y": [y0, y1, None],
|
|
582
661
|
"mode": "lines",
|
|
583
662
|
"line": {"width": 1.5, "color": "#888"},
|
|
584
|
-
"hoverinfo": "none",
|
|
663
|
+
"hoverinfo": "text" if file_path else "none",
|
|
664
|
+
"hovertext": file_path if file_path else None,
|
|
585
665
|
"showlegend": False,
|
|
586
666
|
}
|
|
587
667
|
)
|
|
588
668
|
|
|
669
|
+
# Add annotation at midpoint of edge if we have file path info
|
|
670
|
+
if file_name:
|
|
671
|
+
mid_x = (x0 + x1) / 2
|
|
672
|
+
mid_y = (y0 + y1) / 2
|
|
673
|
+
edge_annotations.append(
|
|
674
|
+
{
|
|
675
|
+
"x": mid_x,
|
|
676
|
+
"y": mid_y,
|
|
677
|
+
"text": file_name,
|
|
678
|
+
"showarrow": False,
|
|
679
|
+
"font": {"size": 9, "color": "#666"},
|
|
680
|
+
"bgcolor": "rgba(255,255,255,0.8)",
|
|
681
|
+
}
|
|
682
|
+
)
|
|
683
|
+
|
|
589
684
|
# Build node trace
|
|
590
685
|
node_x = [positions[n][0] for n in node_list if n in positions]
|
|
591
686
|
node_y = [positions[n][1] for n in node_list if n in positions]
|
|
@@ -597,9 +692,10 @@ class PlotlyFormatter:
|
|
|
597
692
|
"mode": "markers+text",
|
|
598
693
|
"text": node_list,
|
|
599
694
|
"textposition": "top center",
|
|
695
|
+
"textfont": {"size": 11},
|
|
600
696
|
"hoverinfo": "text",
|
|
601
697
|
"marker": {
|
|
602
|
-
"size":
|
|
698
|
+
"size": 15,
|
|
603
699
|
"color": node_colors,
|
|
604
700
|
"line": {"width": 2, "color": "#333"},
|
|
605
701
|
},
|
|
@@ -637,22 +733,32 @@ class PlotlyFormatter:
|
|
|
637
733
|
},
|
|
638
734
|
]
|
|
639
735
|
|
|
640
|
-
direction_label =
|
|
641
|
-
"Upstream" if result.direction == "upstream" else "Downstream"
|
|
642
|
-
)
|
|
736
|
+
direction_label = "Upstream" if result.direction == "upstream" else "Downstream"
|
|
643
737
|
title = f"{direction_label} Lineage: {result.query_column}"
|
|
644
738
|
|
|
739
|
+
# Calculate figure dimensions based on graph size
|
|
740
|
+
min_height = 400
|
|
741
|
+
height_per_node = 50
|
|
742
|
+
calculated_height = max(min_height, len(node_list) * height_per_node)
|
|
743
|
+
|
|
744
|
+
layout: dict[str, Any] = {
|
|
745
|
+
"title": {"text": title},
|
|
746
|
+
"showlegend": True,
|
|
747
|
+
"legend": {"x": 1, "y": 1, "xanchor": "right"},
|
|
748
|
+
"hovermode": "closest",
|
|
749
|
+
"xaxis": {"visible": False},
|
|
750
|
+
"yaxis": {"visible": False},
|
|
751
|
+
"height": calculated_height,
|
|
752
|
+
"margin": {"l": 50, "r": 50, "t": 60, "b": 40},
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
# Add edge annotations if we have file path info
|
|
756
|
+
if edge_annotations:
|
|
757
|
+
layout["annotations"] = edge_annotations
|
|
758
|
+
|
|
645
759
|
figure = {
|
|
646
760
|
"data": edge_traces + [node_trace] + legend_traces,
|
|
647
|
-
"layout":
|
|
648
|
-
"title": {"text": title},
|
|
649
|
-
"showlegend": True,
|
|
650
|
-
"legend": {"x": 1, "y": 1, "xanchor": "right"},
|
|
651
|
-
"hovermode": "closest",
|
|
652
|
-
"xaxis": {"visible": False},
|
|
653
|
-
"yaxis": {"visible": False},
|
|
654
|
-
"margin": {"l": 40, "r": 40, "t": 60, "b": 40},
|
|
655
|
-
},
|
|
761
|
+
"layout": layout,
|
|
656
762
|
}
|
|
657
763
|
|
|
658
764
|
return json.dumps(figure, indent=2)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|