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 = """ """ html_content = html_content.replace('', 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 = "

".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 = """ """ html_content = html_content.replace('', click_js) with open(output_path, "w", encoding="utf-8") as f: f.write(html_content) return output_path