216 lines
8.8 KiB
Python
216 lines
8.8 KiB
Python
import networkx as nx
|
|
from pyvis.network import Network
|
|
import textwrap
|
|
import ast
|
|
import re
|
|
|
|
|
|
class Visualizer:
|
|
def __init__(self):
|
|
pass
|
|
|
|
def build_graph(self, propositions, relations, scores, output_path):
|
|
"""
|
|
دریافت ۳ ساختار مجزا (گزارهها، روابط، امتیازها) و رسم گراف
|
|
"""
|
|
net = Network(height="100vh", width="100%", bgcolor="#1a1a1a", font_color="white", directed=True,
|
|
cdn_resources="in_line")
|
|
|
|
# ساخت دیکشنریهای کمکی برای جستجوی سریع
|
|
prop_dict = {p['proposition_id']: p['proposition_text'] for p in propositions}
|
|
score_dict = {s['relation_id']: s['score'] for s in scores}
|
|
|
|
# 1. اضافه کردن نودها (گزارهها)
|
|
for p_id, text in prop_dict.items():
|
|
wrapped_text = "\n".join(textwrap.wrap(text, width=40))
|
|
net.add_node(
|
|
p_id,
|
|
label=f"گزاره {p_id}",
|
|
title=wrapped_text, # نمایش متن کامل هنگام هاور پیشفرض
|
|
shape="box",
|
|
color={"background": "#264653", "border": "#1b3039", "highlight": {"background": "#2a9d8f"}},
|
|
font={"face": "B Nazanin, Tahoma, Arial", "size": 16, "color": "white"}
|
|
)
|
|
|
|
# 2. اضافه کردن یالها (روابط)
|
|
for rel in relations:
|
|
source = rel['source_proposition_id']
|
|
target = rel['target_proposition_id']
|
|
rel_id = rel['relation_id']
|
|
rel_type = rel['relation_type']
|
|
score = score_dict.get(rel_id, 0)
|
|
|
|
wrapped_rel = "\n".join(textwrap.wrap(rel_type, width=40))
|
|
edge_title = f"امتیاز: {score}\n\nنوع رابطه:\n{wrapped_rel}"
|
|
|
|
if source in prop_dict and target in prop_dict:
|
|
net.add_edge(
|
|
source,
|
|
target,
|
|
label=str(score),
|
|
title=edge_title, # نمایش متن در سایدبار از طریق رویداد
|
|
width=max(2, int(score) / 2), # ضخامت خط بر اساس امتیاز
|
|
color={"color": "#e9c46a", "highlight": "#f4a261"},
|
|
font={"align": "middle", "color": "#ffea00", "background": "#1a1a1a"},
|
|
arrows={"to": {"enabled": True, "scaleFactor": 1.2}}
|
|
)
|
|
|
|
# تنظیمات گراف و فعالسازی هاور
|
|
net.set_options("""
|
|
{
|
|
"physics": {
|
|
"forceAtlas2Based": {
|
|
"gravitationalConstant": -50,
|
|
"centralGravity": 0.01,
|
|
"springLength": 150,
|
|
"springConstant": 0.08
|
|
},
|
|
"minVelocity": 0.75,
|
|
"solver": "forceAtlas2Based"
|
|
},
|
|
"interaction": { "hover": true, "navigationButtons": true }
|
|
}
|
|
""")
|
|
|
|
html_content = net.generate_html(output_path)
|
|
|
|
# تزریق اسکریپت برای ارسال اطلاعات هاور به سایدبار صفحه وب
|
|
hover_js = """
|
|
<script type="text/javascript">
|
|
network.on("hoverEdge", function (params) {
|
|
var edgeData = edges.get(params.edge);
|
|
window.parent.postMessage({ type: 'hoverEdge', info: edgeData.title }, '*');
|
|
});
|
|
network.on("hoverNode", function (params) {
|
|
var nodeData = nodes.get(params.node);
|
|
window.parent.postMessage({ type: 'hoverNode', info: nodeData.title }, '*');
|
|
});
|
|
network.on("blurEdge", function (params) {
|
|
window.parent.postMessage({ type: 'blur', info: '' }, '*');
|
|
});
|
|
network.on("blurNode", function (params) {
|
|
window.parent.postMessage({ type: 'blur', info: '' }, '*');
|
|
});
|
|
</script>
|
|
</body>
|
|
"""
|
|
html_content = html_content.replace('</body>', hover_js)
|
|
|
|
with open(output_path, "w", encoding="utf-8") as f:
|
|
f.write(html_content)
|
|
return output_path
|
|
|
|
def build_tree(self, tree_data_list, output_path):
|
|
net = Network(height="100vh", width="100%", bgcolor="#1a1a1a", font_color="white", directed=True,
|
|
cdn_resources="in_line")
|
|
valid_node_ids = set()
|
|
|
|
for node_data in tree_data_list:
|
|
node_id = str(node_data.get('segment_id', '')).strip()
|
|
valid_node_ids.add(node_id)
|
|
level = int(node_data.get('level', 0))
|
|
|
|
full_text = node_data.get('semantic_unit', 'بدون متن')
|
|
wrapped_text = "\n".join(textwrap.wrap(full_text, width=40))
|
|
|
|
raw_sentences = node_data.get('original_sentences', [])
|
|
if isinstance(raw_sentences, str):
|
|
try:
|
|
raw_sentences = ast.literal_eval(raw_sentences)
|
|
except (ValueError, SyntaxError):
|
|
pass
|
|
|
|
if isinstance(raw_sentences, list):
|
|
sentences_str = "<br><br>".join(raw_sentences)
|
|
num_sentences = len(raw_sentences)
|
|
else:
|
|
sentences_str = str(raw_sentences)
|
|
num_sentences = len([s for s in sentences_str.split('.') if s.strip()]) if sentences_str else 0
|
|
|
|
# متمایز کردن رنگ برگها (سطح 0) از نودهای میانی
|
|
bg_color = "#e76f51" if level == 0 else "#2a9d8f"
|
|
|
|
net.add_node(
|
|
node_id,
|
|
label=f"سطح {level}\n{node_id}",
|
|
title=wrapped_text,
|
|
level=level,
|
|
shape="box",
|
|
sentences=sentences_str,
|
|
num_sentences=num_sentences,
|
|
color={"background": bg_color, "border": "#1f6e64", "highlight": {"background": "#3bc2b4"}},
|
|
font={"face": "B Nazanin, Tahoma, Arial", "size": 16}
|
|
)
|
|
|
|
for node_data in tree_data_list:
|
|
node_id = str(node_data.get('segment_id', '')).strip()
|
|
parent_val = node_data.get('parent_segment_id')
|
|
|
|
if parent_val and str(parent_val).strip() != "":
|
|
clean_parents_str = re.sub(r"[\[\]\'\"\s]", "", str(parent_val))
|
|
parents_list = clean_parents_str.split(',')
|
|
|
|
for p_id in parents_list:
|
|
if p_id in valid_node_ids:
|
|
net.add_edge(
|
|
p_id,
|
|
node_id,
|
|
width=3,
|
|
color={"color": "#e9c46a", "highlight": "#f4a261"},
|
|
arrows={"to": {"enabled": True, "scaleFactor": 1.2}},
|
|
smooth={'type': 'cubicBezier', 'forceDirection': 'vertical', 'roundness': 0.4}
|
|
)
|
|
|
|
net.set_options("""
|
|
{
|
|
"layout": {
|
|
"hierarchical": {
|
|
"enabled": true,
|
|
"direction": "DU",
|
|
"sortMethod": "directed",
|
|
"nodeSpacing": 250,
|
|
"levelSeparation": 150
|
|
}
|
|
},
|
|
"physics": {
|
|
"hierarchicalRepulsion": {
|
|
"centralGravity": 0.0,
|
|
"springLength": 100,
|
|
"springConstant": 0.01,
|
|
"nodeDistance": 250,
|
|
"damping": 0.09
|
|
},
|
|
"solver": "hierarchicalRepulsion"
|
|
},
|
|
"interaction": { "hover": true, "navigationButtons": true }
|
|
}
|
|
""")
|
|
|
|
html_content = net.generate_html(output_path)
|
|
|
|
# تزریق اسکریپت کلیک درخت
|
|
click_js = """
|
|
<script type="text/javascript">
|
|
network.on("click", function (params) {
|
|
if (params.nodes.length > 0) {
|
|
var nodeId = params.nodes[0];
|
|
var clickedNode = nodes.get(nodeId);
|
|
window.parent.postMessage({
|
|
type: 'node_click',
|
|
id: nodeId,
|
|
label: clickedNode.label,
|
|
summary: clickedNode.title,
|
|
sentences: clickedNode.sentences || 'اطلاعات جملات موجود نیست.',
|
|
num_sentences: clickedNode.num_sentences || 0
|
|
}, '*');
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
"""
|
|
html_content = html_content.replace('</body>', click_js)
|
|
|
|
with open(output_path, "w", encoding="utf-8") as f:
|
|
f.write(html_content)
|
|
|
|
return output_path |