From 29c2add74e95d3c3aeab1520a2a7e55c900dc50f Mon Sep 17 00:00:00 2001
From: aliakbar
Date: Mon, 29 Jun 2026 15:32:24 +0330
Subject: [PATCH] First Initialization
---
New Text Document.txt | 46 ++
__pycache__/config.cpython-313.pyc | Bin 0 -> 297 bytes
app.py | 240 +++++++++
config.py | 3 +
modules/__init__.py | 14 +
modules/__pycache__/__init__.cpython-313.pyc | Bin 0 -> 404 bytes
.../__pycache__/api_handler.cpython-313.pyc | Bin 0 -> 8551 bytes
.../__pycache__/data_manager.cpython-313.pyc | Bin 0 -> 4400 bytes
modules/__pycache__/segmenter.cpython-313.pyc | Bin 0 -> 5716 bytes
.../__pycache__/segmenter_old.cpython-313.pyc | Bin 0 -> 4953 bytes
.../__pycache__/visualizer.cpython-313.pyc | Bin 0 -> 9372 bytes
modules/api_handler.py | 125 +++++
modules/data_manager.py | 59 +++
modules/segmenter.py | 116 +++++
modules/visualizer.py | 216 +++++++++
projects/2026-04-17_23-05-31/tree_data.csv | 7 +
projects/2026-04-17_23-20-18/tree_data.csv | 7 +
.../tree_visualization.html | 0
projects/2026-04-17_23-25-21/tree_data.csv | 7 +
.../tree_visualization.html | 341 +++++++++++++
projects/2026-04-17_23-37-02/tree_data.csv | 5 +
.../tree_visualization.html | 360 ++++++++++++++
projects/2026-04-17_23-45-04/tree_data.csv | 7 +
.../tree_visualization.html | 360 ++++++++++++++
projects/2026-04-17_23-46-38/tree_data.csv | 7 +
.../tree_visualization.html | 360 ++++++++++++++
projects/2026-04-17_23-48-07/tree_data.csv | 5 +
.../tree_visualization.html | 360 ++++++++++++++
projects/2026-04-17_23-55-01/tree_data.csv | 7 +
.../tree_visualization.html | 360 ++++++++++++++
projects/2026-04-18_00-03-54/tree_data.csv | 7 +
.../tree_visualization.html | 359 ++++++++++++++
projects/2026-04-18_00-22-06/tree_data.csv | 7 +
.../tree_visualization.html | 359 ++++++++++++++
projects/2026-04-18_00-26-18/tree_data.csv | 5 +
.../tree_visualization.html | 359 ++++++++++++++
.../node_L1_3_graph/graph.html | 359 ++++++++++++++
.../node_L1_3_graph/propositions.csv | 14 +
.../node_L1_3_graph/relations.csv | 12 +
.../node_L1_3_graph/scores.csv | 12 +
projects/2026-04-18_00-53-56/tree_data.csv | 21 +
.../tree_visualization.html | 359 ++++++++++++++
.../node_L1_4_graph/graph.html | 359 ++++++++++++++
.../node_L1_4_graph/propositions.csv | 11 +
.../node_L1_4_graph/relations.csv | 35 ++
.../node_L1_4_graph/scores.csv | 33 ++
projects/2026-04-18_00-58-49/tree_data.csv | 19 +
.../tree_visualization.html | 359 ++++++++++++++
projects/2026-04-22_23-54-42/tree_data.csv | 192 ++++++++
.../tree_visualization.html | 454 ++++++++++++++++++
projects/2026-04-23_00-42-09/tree_data.csv | 196 ++++++++
.../tree_visualization.html | 454 ++++++++++++++++++
.../node_L1_1_graph/graph.html | 359 ++++++++++++++
.../node_L1_1_graph/propositions.csv | 13 +
.../node_L1_1_graph/relations.csv | 15 +
.../node_L1_1_graph/scores.csv | 15 +
.../node_L1_2_graph/graph.html | 359 ++++++++++++++
.../node_L1_2_graph/propositions.csv | 29 ++
.../node_L1_2_graph/relations.csv | 68 +++
.../node_L1_2_graph/scores.csv | 66 +++
.../node_L1_8_graph/graph.html | 359 ++++++++++++++
.../node_L1_8_graph/propositions.csv | 10 +
.../node_L1_8_graph/relations.csv | 13 +
.../node_L1_8_graph/scores.csv | 13 +
.../node_L2_37_graph/graph.html | 359 ++++++++++++++
.../node_L2_37_graph/propositions.csv | 8 +
.../node_L2_37_graph/relations.csv | 6 +
.../node_L2_37_graph/scores.csv | 6 +
.../node_L2_41_graph/graph.html | 359 ++++++++++++++
.../node_L2_41_graph/propositions.csv | 3 +
.../node_L2_41_graph/relations.csv | 1 +
.../node_L2_41_graph/scores.csv | 1 +
.../node_L2_42_graph/graph.html | 359 ++++++++++++++
.../node_L2_42_graph/propositions.csv | 11 +
.../node_L2_42_graph/relations.csv | 2 +
.../node_L2_42_graph/scores.csv | 2 +
.../node_L2_43_graph/graph.html | 359 ++++++++++++++
.../node_L2_43_graph/propositions.csv | 5 +
.../node_L2_43_graph/relations.csv | 2 +
.../node_L2_43_graph/scores.csv | 2 +
.../node_L3_48_graph/graph.html | 359 ++++++++++++++
.../node_L3_48_graph/propositions.csv | 4 +
.../node_L3_48_graph/relations.csv | 1 +
.../node_L3_48_graph/scores.csv | 1 +
.../node_L4_ROOT_graph/graph.html | 359 ++++++++++++++
.../node_L4_ROOT_graph/propositions.csv | 6 +
.../node_L4_ROOT_graph/relations.csv | 1 +
.../node_L4_ROOT_graph/scores.csv | 1 +
projects/2026-04-23_00-45-38/tree_data.csv | 192 ++++++++
.../tree_visualization.html | 454 ++++++++++++++++++
projects/2026-04-23_10-40-24/tree_data.csv | 196 ++++++++
.../tree_visualization.html | 454 ++++++++++++++++++
projects/2026-05-01_13-22-50/tree_data.csv | 197 ++++++++
.../tree_visualization.html | 454 ++++++++++++++++++
static/css/style.css | 213 ++++++++
static/js/main.js | 32 ++
static/js/vis-network.min.js | 49 ++
templates/graph_view.html | 34 ++
templates/index.html | 25 +
templates/loading.html | 14 +
templates/new_project.html | 31 ++
templates/tree_view.html | 59 +++
tests/est_api.py | 45 ++
tests/test_segmenter.py | 37 ++
viewMode_app.py | 158 ++++++
105 files changed, 13177 insertions(+)
create mode 100644 New Text Document.txt
create mode 100644 __pycache__/config.cpython-313.pyc
create mode 100644 app.py
create mode 100644 config.py
create mode 100644 modules/__init__.py
create mode 100644 modules/__pycache__/__init__.cpython-313.pyc
create mode 100644 modules/__pycache__/api_handler.cpython-313.pyc
create mode 100644 modules/__pycache__/data_manager.cpython-313.pyc
create mode 100644 modules/__pycache__/segmenter.cpython-313.pyc
create mode 100644 modules/__pycache__/segmenter_old.cpython-313.pyc
create mode 100644 modules/__pycache__/visualizer.cpython-313.pyc
create mode 100644 modules/api_handler.py
create mode 100644 modules/data_manager.py
create mode 100644 modules/segmenter.py
create mode 100644 modules/visualizer.py
create mode 100644 projects/2026-04-17_23-05-31/tree_data.csv
create mode 100644 projects/2026-04-17_23-20-18/tree_data.csv
create mode 100644 projects/2026-04-17_23-20-18/tree_visualization.html
create mode 100644 projects/2026-04-17_23-25-21/tree_data.csv
create mode 100644 projects/2026-04-17_23-25-21/tree_visualization.html
create mode 100644 projects/2026-04-17_23-37-02/tree_data.csv
create mode 100644 projects/2026-04-17_23-37-02/tree_visualization.html
create mode 100644 projects/2026-04-17_23-45-04/tree_data.csv
create mode 100644 projects/2026-04-17_23-45-04/tree_visualization.html
create mode 100644 projects/2026-04-17_23-46-38/tree_data.csv
create mode 100644 projects/2026-04-17_23-46-38/tree_visualization.html
create mode 100644 projects/2026-04-17_23-48-07/tree_data.csv
create mode 100644 projects/2026-04-17_23-48-07/tree_visualization.html
create mode 100644 projects/2026-04-17_23-55-01/tree_data.csv
create mode 100644 projects/2026-04-17_23-55-01/tree_visualization.html
create mode 100644 projects/2026-04-18_00-03-54/tree_data.csv
create mode 100644 projects/2026-04-18_00-03-54/tree_visualization.html
create mode 100644 projects/2026-04-18_00-22-06/tree_data.csv
create mode 100644 projects/2026-04-18_00-22-06/tree_visualization.html
create mode 100644 projects/2026-04-18_00-26-18/tree_data.csv
create mode 100644 projects/2026-04-18_00-26-18/tree_visualization.html
create mode 100644 projects/2026-04-18_00-53-56/node_L1_3_graph/graph.html
create mode 100644 projects/2026-04-18_00-53-56/node_L1_3_graph/propositions.csv
create mode 100644 projects/2026-04-18_00-53-56/node_L1_3_graph/relations.csv
create mode 100644 projects/2026-04-18_00-53-56/node_L1_3_graph/scores.csv
create mode 100644 projects/2026-04-18_00-53-56/tree_data.csv
create mode 100644 projects/2026-04-18_00-53-56/tree_visualization.html
create mode 100644 projects/2026-04-18_00-58-49/node_L1_4_graph/graph.html
create mode 100644 projects/2026-04-18_00-58-49/node_L1_4_graph/propositions.csv
create mode 100644 projects/2026-04-18_00-58-49/node_L1_4_graph/relations.csv
create mode 100644 projects/2026-04-18_00-58-49/node_L1_4_graph/scores.csv
create mode 100644 projects/2026-04-18_00-58-49/tree_data.csv
create mode 100644 projects/2026-04-18_00-58-49/tree_visualization.html
create mode 100644 projects/2026-04-22_23-54-42/tree_data.csv
create mode 100644 projects/2026-04-22_23-54-42/tree_visualization.html
create mode 100644 projects/2026-04-23_00-42-09/tree_data.csv
create mode 100644 projects/2026-04-23_00-42-09/tree_visualization.html
create mode 100644 projects/2026-04-23_00-45-38/node_L1_1_graph/graph.html
create mode 100644 projects/2026-04-23_00-45-38/node_L1_1_graph/propositions.csv
create mode 100644 projects/2026-04-23_00-45-38/node_L1_1_graph/relations.csv
create mode 100644 projects/2026-04-23_00-45-38/node_L1_1_graph/scores.csv
create mode 100644 projects/2026-04-23_00-45-38/node_L1_2_graph/graph.html
create mode 100644 projects/2026-04-23_00-45-38/node_L1_2_graph/propositions.csv
create mode 100644 projects/2026-04-23_00-45-38/node_L1_2_graph/relations.csv
create mode 100644 projects/2026-04-23_00-45-38/node_L1_2_graph/scores.csv
create mode 100644 projects/2026-04-23_00-45-38/node_L1_8_graph/graph.html
create mode 100644 projects/2026-04-23_00-45-38/node_L1_8_graph/propositions.csv
create mode 100644 projects/2026-04-23_00-45-38/node_L1_8_graph/relations.csv
create mode 100644 projects/2026-04-23_00-45-38/node_L1_8_graph/scores.csv
create mode 100644 projects/2026-04-23_00-45-38/node_L2_37_graph/graph.html
create mode 100644 projects/2026-04-23_00-45-38/node_L2_37_graph/propositions.csv
create mode 100644 projects/2026-04-23_00-45-38/node_L2_37_graph/relations.csv
create mode 100644 projects/2026-04-23_00-45-38/node_L2_37_graph/scores.csv
create mode 100644 projects/2026-04-23_00-45-38/node_L2_41_graph/graph.html
create mode 100644 projects/2026-04-23_00-45-38/node_L2_41_graph/propositions.csv
create mode 100644 projects/2026-04-23_00-45-38/node_L2_41_graph/relations.csv
create mode 100644 projects/2026-04-23_00-45-38/node_L2_41_graph/scores.csv
create mode 100644 projects/2026-04-23_00-45-38/node_L2_42_graph/graph.html
create mode 100644 projects/2026-04-23_00-45-38/node_L2_42_graph/propositions.csv
create mode 100644 projects/2026-04-23_00-45-38/node_L2_42_graph/relations.csv
create mode 100644 projects/2026-04-23_00-45-38/node_L2_42_graph/scores.csv
create mode 100644 projects/2026-04-23_00-45-38/node_L2_43_graph/graph.html
create mode 100644 projects/2026-04-23_00-45-38/node_L2_43_graph/propositions.csv
create mode 100644 projects/2026-04-23_00-45-38/node_L2_43_graph/relations.csv
create mode 100644 projects/2026-04-23_00-45-38/node_L2_43_graph/scores.csv
create mode 100644 projects/2026-04-23_00-45-38/node_L3_48_graph/graph.html
create mode 100644 projects/2026-04-23_00-45-38/node_L3_48_graph/propositions.csv
create mode 100644 projects/2026-04-23_00-45-38/node_L3_48_graph/relations.csv
create mode 100644 projects/2026-04-23_00-45-38/node_L3_48_graph/scores.csv
create mode 100644 projects/2026-04-23_00-45-38/node_L4_ROOT_graph/graph.html
create mode 100644 projects/2026-04-23_00-45-38/node_L4_ROOT_graph/propositions.csv
create mode 100644 projects/2026-04-23_00-45-38/node_L4_ROOT_graph/relations.csv
create mode 100644 projects/2026-04-23_00-45-38/node_L4_ROOT_graph/scores.csv
create mode 100644 projects/2026-04-23_00-45-38/tree_data.csv
create mode 100644 projects/2026-04-23_00-45-38/tree_visualization.html
create mode 100644 projects/2026-04-23_10-40-24/tree_data.csv
create mode 100644 projects/2026-04-23_10-40-24/tree_visualization.html
create mode 100644 projects/2026-05-01_13-22-50/tree_data.csv
create mode 100644 projects/2026-05-01_13-22-50/tree_visualization.html
create mode 100644 static/css/style.css
create mode 100644 static/js/main.js
create mode 100644 static/js/vis-network.min.js
create mode 100644 templates/graph_view.html
create mode 100644 templates/index.html
create mode 100644 templates/loading.html
create mode 100644 templates/new_project.html
create mode 100644 templates/tree_view.html
create mode 100644 tests/est_api.py
create mode 100644 tests/test_segmenter.py
create mode 100644 viewMode_app.py
diff --git a/New Text Document.txt b/New Text Document.txt
new file mode 100644
index 0000000..17c131b
--- /dev/null
+++ b/New Text Document.txt
@@ -0,0 +1,46 @@
+NLP_Graph_Project/
+│
+├── app.py # فایل اصلی وبسرور (مدیریت روتها و صفحات وب)
+├── config.py # تنظیمات کلی (مسیر مدلها، کلید API و ...)
+│
+├── modules/ # پوشه ماژولهای پایتونی
+│ ├── __init__.py
+│ ├── segmenter.py # ماژول ۱: سگمنتبندی با مدلهای لوکال (Gap Cosine)
+│ ├── api_handler.py # ماژول ۲: ارتباط با LLM برای استخراج گزاره، روابط و امتیازدهی
+│ ├── visualizer.py # ماژول ۳: رسم درخت و گراف تعاملی با PyVis و Textwrap
+│ └── data_manager.py # مدیریت فایلهای CSV و پوشههای پروژه
+│
+├── static/ # فایلهای استاتیک وب (آفلاین)
+│ ├── css/
+│ │ └── style.css # استایلهای صفحات وب
+│ ├── js/
+│ │ ├── main.js # کدهای جاوااسکریپت برای مدیریت کلیکها و درخواستهای Ajax
+│ │ └── vis-network.min.js # کتابخانه آفلاین PyVis
+│ └── images/
+│
+├── templates/ # صفحات HTML (بخش وب برنامه)
+│ ├── index.html # صفحه ۱: داشبورد و لیست پروژههای گذشته
+│ ├── new_project.html # صفحه ۲: دریافت متن و تعداد سگمنتهای نهایی
+│ ├── loading.html # صفحه ۳: صفحه انتظار و نمایش وضعیت پردازش
+│ ├── tree_view.html # صفحه ۴: نمایش درخت، سایدبار و هندل کردن کلیک روی نودها
+│ └── graph_view.html # صفحه ۵: نمایش گراف نهایی
+│
+├── projects/ # محل ذخیره دیتای پروژهها
+│ ├── 1405-01-28_10-30-00/ # نمونه یک پوشه پروژه بر اساس تاریخ و زمان
+│ │ ├── tree_data.csv # اطلاعات ساختار درخت و سگمنتها
+│ │ ├── tree_visualization.html # فایل آفلاین درخت
+│ │ ├── node_ID_1_graph/ # دیتای مربوط به گراف ساخته شده برای یک نود خاص
+│ │ │ ├── propositions.csv
+│ │ │ ├── relations.csv
+│ │ │ ├── scores.csv
+│ │ │ └── graph.html
+│ │ └── node_ID_2_graph/ # گراف نود دیگر در صورت کلیک کاربر
+│ │ └── ...
+│ └── ...
+│
+├── tests/ # پوشه تست برای ماژولها
+│ ├── test_segmenter.py
+│ ├── test_api.py
+│ └── test_visualizer.py
+│
+└── requirements.txt # لیست پیشنیازهای پروژه
diff --git a/__pycache__/config.cpython-313.pyc b/__pycache__/config.cpython-313.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..60f623750839bc8494418e90c5f13cbbed639d9f
GIT binary patch
literal 297
zcmey&%ge<81PgRtX3hiBk3k$5V1zP03ji5Y8G;##7=js#7=xLLn9`XwS*nbSvvosF
z+{3G~3qveR{PV(#+>?_Gk}AEjO{=oPGqMA-!YxdT3scjR!h`d&1C5M){Cuh;GfGMd
zimmkZ6ALo+(i02P3rh473kvkh46E3HY+aLlKTYOa?2ZAR@!qbHw>X>}gI(i8gM4mr
z`TD!K`o#M=`ns-U_zZH|ElX#sn9$a1yVgpJ50C2HZA^-pY
literal 0
HcmV?d00001
diff --git a/app.py b/app.py
new file mode 100644
index 0000000..6326088
--- /dev/null
+++ b/app.py
@@ -0,0 +1,240 @@
+import os
+import csv
+import json
+import itertools
+from datetime import datetime
+from flask import Flask, render_template, request, redirect, url_for, send_file
+
+# وارد کردن ماژولهای پروژه
+from modules.segmenter import Segmenter
+from modules.api_handler import APIHandler
+from modules.data_manager import DataManager
+from modules.visualizer import Visualizer
+
+app = Flask(__name__)
+
+# مقداردهی اولیه ماژولها
+segmenter = Segmenter()
+api_handler = APIHandler()
+data_manager = DataManager()
+visualizer = Visualizer()
+
+
+@app.route('/', methods=['GET'])
+def index():
+ """نمایش لیست پروژههای قبلی در داشبورد"""
+ projects_dir = data_manager.base_projects_dir
+ projects = []
+ if os.path.exists(projects_dir):
+ projects = [d for d in os.listdir(projects_dir) if os.path.isdir(os.path.join(projects_dir, d))]
+ projects.sort(reverse=True)
+ return render_template('index.html', projects=projects)
+
+
+@app.route('/new_project', methods=['GET'])
+def new_project():
+ """فرم دریافت متن جدید"""
+ return render_template('new_project.html')
+
+
+@app.route('/process', methods=['POST'])
+def process_text():
+ """اجرای الگوریتم پایین به بالا و ساخت درخت با یک ریشه کلان (ROOT)"""
+ text = request.form.get('text')
+ target_count = int(request.form.get('target_segments', 7))
+
+ if not text:
+ return redirect(url_for('new_project'))
+
+ project_name = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
+ project_path = data_manager.create_project_folder(project_name)
+
+ # مرحله ۱: استخراج جملات خام اولیه
+ sentences = segmenter.extract_sentences(text)
+
+ tree_data_list = []
+ current_level_items = []
+
+ for i, s in enumerate(sentences):
+ node_id = f"node_L0_{i}"
+ current_level_items.append({"id": node_id, "text": s})
+ tree_data_list.append({
+ "level": 0,
+ "segment_id": node_id,
+ "parent_segment_id": "",
+ "original_sentences": [s],
+ "semantic_unit": s
+ })
+
+ level = 1
+ global_node_counter = 1
+
+ # مرحله ۲: سگمنتبندی بازگشتی تا رسیدن به تعداد هدف (مثلا 7 سگمنت)
+ while len(current_level_items) > target_count:
+ texts_to_segment = [item["text"] for item in current_level_items]
+
+ # سگمنتبندی داینامیک
+ grouped_segments_texts = segmenter.segment_items(texts_to_segment)
+
+ # جلوگیری از پرش ناگهانی: اگر تعداد سگمنتها کمتر از هدف شد، آن را وادار کن دقیقاً به اندازه هدف برش بزند
+ if len(grouped_segments_texts) <= target_count:
+ grouped_segments_texts = segmenter.segment_items(texts_to_segment, force_count=target_count)
+
+ next_level_items = []
+ pointer = 0
+
+ for group in grouped_segments_texts:
+ parent_node_id = f"node_L{level}_{global_node_counter}"
+ global_node_counter += 1
+
+ children_ids = []
+ segment_content = []
+
+ for _ in group:
+ child = current_level_items[pointer]
+ children_ids.append(child["id"])
+ segment_content.append(child["text"])
+
+ # ثبت آیدی پدر برای نودهای فرزند
+ for entry in tree_data_list:
+ if entry["segment_id"] == child["id"]:
+ entry["parent_segment_id"] = parent_node_id
+ pointer += 1
+
+ # استخراج واحد معنایی
+ semantic_unit = api_handler.get_semantic_unit(segment_content)
+
+ next_level_items.append({"id": parent_node_id, "text": semantic_unit})
+ tree_data_list.append({
+ "level": level,
+ "segment_id": parent_node_id,
+ "parent_segment_id": "",
+ "original_sentences": segment_content,
+ "semantic_unit": semantic_unit
+ })
+
+ current_level_items = next_level_items
+ level += 1
+
+ # =========================================================================
+ # مرحله ۳: ساخت ریشه نهایی (ROOT) برای کل متن
+ # اتصال تمامی سگمنتهای نهایی (مثلا 7 تا) به یک رأس کلی و گرفتن معنای کلان
+ # =========================================================================
+ if len(current_level_items) > 1:
+ root_node_id = f"node_L{level}_ROOT"
+ root_content = []
+
+ for item in current_level_items:
+ root_content.append(item["text"])
+
+ for entry in tree_data_list:
+ if entry["segment_id"] == item["id"]:
+ entry["parent_segment_id"] = root_node_id
+
+ # ارسال کل 7 واحد معنایی به API برای دریافت غایت نهایی متن
+ root_semantic_unit = api_handler.get_semantic_unit(root_content)
+
+ tree_data_list.append({
+ "level": level,
+ "segment_id": root_node_id,
+ "parent_segment_id": "", # این نود پدر ندارد
+ "original_sentences": root_content,
+ "semantic_unit": root_semantic_unit
+ })
+
+ # ذخیرهسازی دادههای درخت و رسم آن
+ data_manager.save_tree_data(project_path, tree_data_list)
+ tree_html_path = os.path.join(project_path, "tree_visualization.html")
+ visualizer.build_tree(tree_data_list, tree_html_path)
+
+ return redirect(url_for('show_tree', project_name=project_name))
+
+
+@app.route('/tree/')
+def show_tree(project_name):
+ """نمایش صفحه وب حاوی درخت سلسلهمراتبی"""
+ return render_template('tree_view.html', project_name=project_name)
+
+
+@app.route('/tree_iframe/')
+def tree_iframe(project_name):
+ """سرو کردن فایل HTML درخت برای نمایش در iframe"""
+ project_path = os.path.join(data_manager.base_projects_dir, project_name)
+ tree_html_path = os.path.join(project_path, "tree_visualization.html")
+ return send_file(tree_html_path)
+
+
+@app.route('/graph//')
+def generate_and_show_graph(project_name, node_id):
+ """ساخت گراف On-Demand در زمان کلیک روی نود (حتی نود ROOT)"""
+ project_path = os.path.join(data_manager.base_projects_dir, project_name)
+ graph_dir = os.path.join(project_path, f"{node_id}_graph")
+ graph_html_path = os.path.join(graph_dir, "graph.html")
+
+ # اگر از قبل ساخته شده بود، مجدداً پردازش نکن
+ if os.path.exists(graph_html_path):
+ return render_template('graph_view.html', project_name=project_name, node_id=node_id)
+
+ # خواندن جملات نود از CSV
+ node_sentences = []
+ tree_csv = os.path.join(project_path, "tree_data.csv")
+ with open(tree_csv, mode='r', encoding='utf-8') as f:
+ reader = csv.DictReader(f)
+ for row in reader:
+ if row["segment_id"] == node_id:
+ node_sentences = json.loads(row["original_sentences"])
+ break
+
+ if not node_sentences:
+ return "خطا: دیتای نود یافت نشد.", 404
+
+ # ۱. استخراج گزارهها از API
+ raw_props = api_handler.extract_propositions(node_sentences)
+ propositions = [
+ {"proposition_id": i + 1, "sentence": " ".join(node_sentences), "proposition_text": p}
+ for i, p in enumerate(raw_props)
+ ]
+
+ # ۲. بررسی تمام روابط دو به دو بین گزارهها
+ relations = []
+ scores = []
+ rel_id_counter = 1
+
+ for p1, p2 in itertools.combinations(propositions, 2):
+ rel_desc = api_handler.check_relation(p1["proposition_text"], p2["proposition_text"])
+
+ if rel_desc and "ارتباط وجود ندارد" not in rel_desc:
+ score = api_handler.score_relation(rel_desc)
+ if score > 0:
+ relations.append({
+ "relation_id": rel_id_counter,
+ "source_proposition_id": p1["proposition_id"],
+ "target_proposition_id": p2["proposition_id"],
+ "relation_type": rel_desc
+ })
+ scores.append({
+ "relation_id": rel_id_counter,
+ "score": score
+ })
+ rel_id_counter += 1
+
+ # ۳. ذخیره و رسم گراف
+ data_manager.save_graph_data(project_path, node_id, propositions, relations, scores)
+ visualizer.build_graph(propositions, relations, scores, graph_html_path)
+
+ return render_template('graph_view.html', project_name=project_name, node_id=node_id)
+
+
+@app.route('/graph_iframe//')
+def graph_iframe(project_name, node_id):
+ """سرو کردن فایل HTML گراف برای نمایش در iframe"""
+ project_path = os.path.join(data_manager.base_projects_dir, project_name)
+ graph_html_path = os.path.join(project_path, f"{node_id}_graph", "graph.html")
+ return send_file(graph_html_path)
+
+
+if __name__ == '__main__':
+ for folder in ["projects", "templates", "static"]:
+ if not os.path.exists(folder):
+ os.makedirs(folder)
+ app.run(debug=True, port=5000)
\ No newline at end of file
diff --git a/config.py b/config.py
new file mode 100644
index 0000000..6571d22
--- /dev/null
+++ b/config.py
@@ -0,0 +1,3 @@
+API_KEY = "sk-U4GWzkqT9tOnWrGcb0byJk5zjWhkQjW85sqegbWSnkQ22LNL"
+BASE_URL = "https://api.gapgpt.app/v1"
+MODEL_NAME = "gpt-4o" #"gapgpt-qwen-3.5" or OpenAI what ever
diff --git a/modules/__init__.py b/modules/__init__.py
new file mode 100644
index 0000000..77ee5c3
--- /dev/null
+++ b/modules/__init__.py
@@ -0,0 +1,14 @@
+# modules/__init__.py
+
+from .segmenter_old import Segmenter
+from .api_handler import APIHandler
+from .data_manager import DataManager
+from .visualizer import Visualizer
+
+# تعریف صریح کلاسهایی که با ایمپورت پکیج در دسترس قرار میگیرند
+__all__ = [
+ 'Segmenter',
+ 'APIHandler',
+ 'DataManager',
+ 'Visualizer'
+]
diff --git a/modules/__pycache__/__init__.cpython-313.pyc b/modules/__pycache__/__init__.cpython-313.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..0f6f8d15b280ff5c9c9ba40f63b093fc06677b95
GIT binary patch
literal 404
zcmX|-yGjE=6o$|2H6||hRza~yqolAgOER)IW;TJ?3BH04;VTWK
zvPvfgd(X}qPx15r=kgV269iqriqF>KjR1T$VO!Rp*^JPniolCQtyh_{FWDd
z+72Iun)6J&U~;65=hA7!ZNx*l8|>&nZE>v360@ayyb^5IV$F%(O_0p;7Ht5!yZEOY
znD!atS;p80Y>*9lJU*unDwT@zT*x7W5?A}cSddZLhd26d4bwT`LYQiKOT^y6&$Vr_2Aocd)BKj104SXIpk}Y
literal 0
HcmV?d00001
diff --git a/modules/__pycache__/api_handler.cpython-313.pyc b/modules/__pycache__/api_handler.cpython-313.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..80773f7977d26067a6aa8e2b8c1f2af33b3fdba8
GIT binary patch
literal 8551
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
literal 0
HcmV?d00001
diff --git a/modules/__pycache__/data_manager.cpython-313.pyc b/modules/__pycache__/data_manager.cpython-313.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..66d57b0c0b397364327b1880e568c3049cfe8f1a
GIT binary patch
literal 4400
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
aG~Oocw+YK_!u+lAgicfUHGy-bBmWQR8V#QS
literal 0
HcmV?d00001
diff --git a/modules/api_handler.py b/modules/api_handler.py
new file mode 100644
index 0000000..fa599d9
--- /dev/null
+++ b/modules/api_handler.py
@@ -0,0 +1,125 @@
+import json
+import re
+from openai import OpenAI
+from config import API_KEY, BASE_URL, MODEL_NAME
+
+
+class APIHandler:
+ def __init__(self):
+ # ایجاد کلاینت با استفاده از کتابخانه رسمی OpenAI
+ self.client = OpenAI(
+ base_url=BASE_URL,
+ api_key=API_KEY
+ )
+ self.model_name = MODEL_NAME
+
+ def _call_api(self, prompt, system_prompt="شما یک دستیار هوش مصنوعی دقیق در تحلیل متن هستید.", temperature=0.3):
+
+ try:
+ response = self.client.chat.completions.create(
+ model=self.model_name,
+ messages=[
+ {"role": "system", "content": system_prompt},
+ {"role": "user", "content": prompt}
+ ],
+ temperature=temperature,
+ timeout=60 # جلوگیری از گیر کردن برنامه در صورت کندی سرور
+ )
+ # استخراج محتوای متنی از ریسپانس استاندارد openai
+ return response.choices[0].message.content.strip()
+ except Exception as e:
+ print(f"API failed {e}")
+ return None
+
+ def get_semantic_unit(self, segment_sentences):
+ """تابع ۱: استخراج واحد معنایی (برای جایگزینی در نودهای درخت)"""
+ text_to_summarize = " ".join(segment_sentences)
+
+ prompt = f"""تو یک دستیار هوش مصنوعی متخصص در تحلیل متن و زبانشناسی پردازشی هستی.
+ وظیفه تو این است که متن ورودی را بخوانی و تنها «هسته اصلی واحد معنایی» آن را سنتز و تولید کنی.
+
+ تعریف واحد معنایی مورد نظر: یک تکجملهٔ خلاصه، دقیق و جامع که یک رویداد کامل را توصیف کند. این جمله باید حتماً دارای ساختار منطقی (شامل عامل/فاعل، مفعول و فعلِ نشاندهنده نتیجه) باشد.
+
+ قوانین اکید خروجی (Strict Rules):
+ 1. به هیچوجه از کلمات ربط اضافی، پیشدرآمد (مثل "جمله استخراج شده:"، "خلاصه متن:" یا "بله، حتما") و توضیحات استفاده نکن.
+
+ مثالها (Few-Shot Examples):
+ متن ورودی: "دیروز در پی بارش شدید باران، رودخانه طغیان کرد. این اتفاق باعث شد تا تمام خانههای روستای مجاور زیر آب بروند و مردم مجبور به تخلیه سریع منطقه شوند."
+ خروجی: طغیان رودخانه بر اثر بارش باران منجر به آبگرفتگی خانهها و تخلیه روستا شد.
+
+ متن ورودی: "تیم توسعهدهنده ماهها روی بهینهسازی الگوریتم کار کرد. در نهایت موفق شدند زمان پردازش دادهها را به نصف کاهش دهند که این موضوع رضایت مدیران را در پی داشت."
+ خروجی: تلاش تیم توسعهدهنده برای بهینهسازی الگوریتم باعث کاهش نصف زمان پردازش و جلب رضایت مدیران شد.
+
+
+ متن ورودی اصلی:
+ {text_to_summarize}
+ """
+ response = self._call_api(prompt)
+ return response if response else "خطا در استخراج واحد معنایی"
+
+ def extract_propositions(self, sentences):
+ """تابع ۲: استخراج تکتک گزارهها از جملات یک سگمنت (برای شروع ساخت گراف)"""
+ text = " ".join(sentences)
+
+ prompt = f"""
+ متن زیر را بخوان و تمام گزارههای معنایی (اقدامات، واقعیتها یا مفاهیم مستقل) موجود در آن را استخراج کن.
+ قانون اکید: خروجی را فقط به صورت یک آرایه JSON معتبر از رشتهها برگردان و هیچ متن اضافهای ننویس.
+ مثال خروجی: ["گزاره اول", "گزاره دوم"]
+
+ متن:
+ {text}
+ """
+ response = self._call_api(prompt, temperature=0.2)
+ if not response:
+ return []
+
+ try:
+ clean_json = re.sub(r'```json|```', '', response).strip()
+ propositions = json.loads(clean_json)
+ if isinstance(propositions, list):
+ return propositions
+ return []
+ except Exception as e:
+ print(f"خطا در پارس کردن گزارهها (JSON Error): {e}")
+ print(f"خروجی خام مدل این بود: {response}")
+ return []
+
+ def check_relation(self, prop1, prop2):
+ """تابع ۳: بررسی رابطه غایی (هدف-وسیله) یا علت و معلولی بین دو گزاره"""
+ prompt = f"""
+ دو گزاره زیر را بررسی کن و ببین آیا رابطه "علت و معلولی" یا "غایی (هدف و وسیله)" بین آنها برقرار است یا خیر.
+ اگر هیچ ارتباطی از این نوع وجود ندارد، دقیقاً و فقط بنویس: "ارتباط وجود ندارد"
+ اگر ارتباط وجود دارد، در یک گزاره کوتاه و دقیق این رابطه را توصیف کن (مثلاً: گزاره اول وسیلهای برای رسیدن به هدف گزاره دوم است).
+
+ گزاره ۱: {prop1}
+ گزاره ۲: {prop2}
+ """
+ response = self._call_api(prompt, temperature=0.3)
+ return response if response else "ارتباط وجود ندارد"
+
+ def score_relation(self, relation_desc):
+ """تابع ۴: امتیازدهی به رابطه از 1 تا 10"""
+ if not relation_desc or relation_desc.strip() == "ارتباط وجود ندارد" or relation_desc.strip() == "":
+ return 0
+
+ prompt = f"""
+ با توجه به توصیف رابطه زیر، یک نمره از 1 تا 10 به میزان قوت و منطقی بودن این رابطه علت و معلولی/غایی بده.
+ قانون اکید: فـــقـــط یک عدد صحیح برگردان. هیچ کاراکتر یا کلمهای غیر از عدد ننویس.
+
+ توصیف رابطه:
+ {relation_desc}
+ """
+ response = self._call_api(prompt, temperature=0.1)
+
+ if not response:
+ return 0
+
+ try:
+ numbers = re.findall(r'\d+', response)
+ if numbers:
+ score = int(numbers[0])
+ return max(1, min(10, score))
+ return 0
+ except Exception as e:
+ print(f"failed to extract scores {e}")
+ return 0
\ No newline at end of file
diff --git a/modules/data_manager.py b/modules/data_manager.py
new file mode 100644
index 0000000..dd56953
--- /dev/null
+++ b/modules/data_manager.py
@@ -0,0 +1,59 @@
+import os
+import csv
+import json
+
+
+class DataManager:
+ def __init__(self, base_projects_dir="projects"):
+ self.base_projects_dir = base_projects_dir
+ if not os.path.exists(self.base_projects_dir):
+ os.makedirs(self.base_projects_dir)
+
+ def create_project_folder(self, project_name):
+ """ایجاد پوشه اصلی پروژه"""
+ project_path = os.path.join(self.base_projects_dir, project_name)
+ if not os.path.exists(project_path):
+ os.makedirs(project_path)
+ return project_path
+
+ def save_tree_data(self, project_path, tree_data_list):
+ """ذخیره اطلاعات کل درخت در tree_data.csv"""
+ file_path = os.path.join(project_path, "tree_data.csv")
+ headers = ["level", "segment_id", "parent_segment_id", "original_sentences", "semantic_unit"]
+
+ with open(file_path, mode='w', newline='', encoding='utf-8') as f:
+ writer = csv.DictWriter(f, fieldnames=headers)
+ writer.writeheader()
+ for row in tree_data_list:
+ # تبدیل لیست جملات به رشته JSON برای ذخیره امن در CSV
+ row_copy = row.copy()
+ if isinstance(row_copy.get("original_sentences"), list):
+ row_copy["original_sentences"] = json.dumps(row_copy["original_sentences"], ensure_ascii=False)
+ writer.writerow(row_copy)
+
+ def save_graph_data(self, project_path, node_id, propositions, relations, scores):
+ """ذخیره ۳ فایل CSV مربوط به گراف یک نود خاص"""
+ graph_dir = os.path.join(project_path, f"{node_id}_graph")
+ if not os.path.exists(graph_dir):
+ os.makedirs(graph_dir)
+
+ # 1. save propositions.csv
+ with open(os.path.join(graph_dir, "propositions.csv"), mode='w', newline='', encoding='utf-8') as f:
+ writer = csv.DictWriter(f, fieldnames=["proposition_id", "sentence", "proposition_text"])
+ writer.writeheader()
+ writer.writerows(propositions)
+
+ # 2. save relations.csv
+ with open(os.path.join(graph_dir, "relations.csv"), mode='w', newline='', encoding='utf-8') as f:
+ writer = csv.DictWriter(f, fieldnames=["relation_id", "source_proposition_id", "target_proposition_id",
+ "relation_type"])
+ writer.writeheader()
+ writer.writerows(relations)
+
+ # 3. save scores.csv
+ with open(os.path.join(graph_dir, "scores.csv"), mode='w', newline='', encoding='utf-8') as f:
+ writer = csv.DictWriter(f, fieldnames=["relation_id", "score"])
+ writer.writeheader()
+ writer.writerows(scores)
+
+ return graph_dir
diff --git a/modules/segmenter.py b/modules/segmenter.py
new file mode 100644
index 0000000..6ef0acd
--- /dev/null
+++ b/modules/segmenter.py
@@ -0,0 +1,116 @@
+import re
+import numpy as np
+from sentence_transformers import SentenceTransformer
+from sklearn.metrics.pairwise import cosine_similarity
+
+
+class Segmenter:
+ def __init__(self):
+ print("Loading models :")
+
+ self.model_bge = SentenceTransformer("C:/Models/bge-m3", model_kwargs={"use_safetensors": False})
+ self.model_e5 = SentenceTransformer("C:/Models/multilingual-e5-large")
+ self.model_labse = SentenceTransformer("C:/Models/LaBSE_Local")
+ self.model_parsroberta = SentenceTransformer("C:/Models/ParsRoberta")
+
+ print("4 models loaded successfully")
+
+ def get_similarities(self, text1, text2):
+ """محاسبه میانگین شباهت کسینوسی از ۴ مدل برای دو متن داده شده"""
+ emb_bge = self.model_bge.encode([text1, text2])
+ emb_e5 = self.model_e5.encode([text1, text2])
+ emb_labse = self.model_labse.encode([text1, text2])
+ emb_parsroberta = self.model_parsroberta.encode([text1, text2])
+
+ sim_bge = cosine_similarity([emb_bge[0]], [emb_bge[1]])[0][0]
+ sim_e5 = cosine_similarity([emb_e5[0]], [emb_e5[1]])[0][0]
+ sim_labse = cosine_similarity([emb_labse[0]], [emb_labse[1]])[0][0]
+ sim_parsroberta = cosine_similarity([emb_parsroberta[0]], [emb_parsroberta[1]])[0][0]
+
+ # میانگینگیری از هر ۴ مدل
+ return np.mean([sim_bge, sim_e5, sim_labse, sim_parsroberta])
+
+ def calculate_depth_scores(self, similarities):
+ """محاسبه عمق درهها (Gap Scores) با استفاده از قلههای چپ و راست"""
+ depth_scores = []
+ n = len(similarities)
+
+ for i in range(n):
+ left_peak = similarities[i]
+ for j in range(i, -1, -1):
+ if similarities[j] >= left_peak:
+ left_peak = similarities[j]
+ else:
+ break
+
+ right_peak = similarities[i]
+ for j in range(i, n):
+ if similarities[j] >= right_peak:
+ right_peak = similarities[j]
+ else:
+ break
+
+ depth = (left_peak - similarities[i]) + (right_peak - similarities[i])
+ depth_scores.append(depth)
+
+ return depth_scores
+
+ def extract_sentences(self, text):
+ """جداسازی متن اولیه به جملات"""
+ text = text.replace('\n', ' ')
+ sentences = [s.strip() for s in re.split(r'(?<=[.!?؟]) +', text) if s.strip()]
+ return sentences
+
+ def segment_items(self, items, force_count=None, std_multiplier=0.5, min_depth=0.02):
+ """
+ دریافت جملات یا واحدهای معنایی و کلاسترینگ آنها.
+ اگر پارامتر force_count ارسال شود، الگوریتم موظف است دقیقاً همان تعداد سگمنت تولید کند.
+ """
+ n_items = len(items)
+ if n_items < 2:
+ return [items]
+
+ similarities = []
+ for i in range(n_items - 1):
+ sim = self.get_similarities(items[i], items[i + 1])
+ similarities.append(sim)
+
+ depth_scores = self.calculate_depth_scores(similarities)
+
+ # -------------------------------------------------------------
+ # حالت اجباری: رساندن تعداد سگمنتها به عدد دقیق (مثلا 7)
+ # -------------------------------------------------------------
+ if force_count and 1 < force_count < n_items:
+ # پیدا کردن عمیقترین درهها برای ایجاد دقیقا force_count - 1 برش
+ top_cut_indices = np.argsort(depth_scores)[-(force_count - 1):]
+ top_cut_indices = sorted(top_cut_indices)
+
+ grouped_segments = []
+ start = 0
+ for idx in top_cut_indices:
+ grouped_segments.append(items[start:idx + 1])
+ start = idx + 1
+ grouped_segments.append(items[start:])
+ return grouped_segments
+
+ # -------------------------------------------------------------
+ # حالت عادی داینامیک: در سطوح پایینتر که هنوز به تعداد هدف نرسیدهایم
+ # -------------------------------------------------------------
+ mean_depth = np.mean(depth_scores)
+ std_depth = np.std(depth_scores)
+ cutoff_threshold = mean_depth + (std_multiplier * std_depth)
+
+ grouped_segments = []
+ current_segment = [items[0]]
+
+ for i in range(len(similarities)):
+ if depth_scores[i] >= cutoff_threshold and depth_scores[i] > min_depth:
+ grouped_segments.append(current_segment)
+ current_segment = [items[i + 1]]
+ else:
+ current_segment.append(items[i + 1])
+
+ if current_segment:
+ grouped_segments.append(current_segment)
+
+ return grouped_segments
\ No newline at end of file
diff --git a/modules/visualizer.py b/modules/visualizer.py
new file mode 100644
index 0000000..fa95e0c
--- /dev/null
+++ b/modules/visualizer.py
@@ -0,0 +1,216 @@
+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
\ No newline at end of file
diff --git a/projects/2026-04-17_23-05-31/tree_data.csv b/projects/2026-04-17_23-05-31/tree_data.csv
new file mode 100644
index 0000000..ec28cab
--- /dev/null
+++ b/projects/2026-04-17_23-05-31/tree_data.csv
@@ -0,0 +1,7 @@
+level,segment_id,parent_segment_id,original_sentences,semantic_unit
+0,node_1,,"[""ظهور دادهها و رشد قدرت محاسباتی، به همراه روشهای یادگیری نوین، منجر به دستاوردهای قابل توجهی در بسیاری از حوزههای علمی شده است."", ""در چند سال اخیر، یادگیری ماشینی و هوش مصنوعی یک موج جدید از توجهات را به خود جلب کردهاند که علت اصلی آن، حجم عظیم و روزافزون داده و قدرت محاسباتی، و همچنین کشف الگوریتمهای یادگیری بهبود یافته، می¬باشد[1]."", ""با این حال، ایدهی یادگیری یک رایانه از مجموعه¬ای از مفاهیم انتزاعی و تبدیل داده¬ها به اطلاعات و استفاده از آنها برای پیش بینی، جدید نیست و حداقل از دهه ۱۹۵۰ زمانی که اولین شبکههای عصبی توسعه یافتند، وجود داشته است [2]."", ""همان¬گونه که ذکر شد، حجم زیاد داده¬ها ما را به دوران جدیدی از رشد نمایی در حجم و پیچیدگی دادهها وارد کرده است؛ اینترنت، تلفنهای همراه، کامپیوترها و تجهیزات ضبط باعث ظهور معدن¬های بزرگ داده شده و انسان را به یک تغییر شیب در فرآیند تحقیقات سوق می¬دهد [3]."", ""دانش مدفون در این معدن داده نیازمند فناوریهای کاوش داده است تا الگوهای جالب، معنیدار و قوی را کشف کند."", ""این روش جایگزین تحقیقات نسبت به رویکرد سنتی، اثرات چشمگیری بر همه حوزههای تحقیقات داشته است."", ""کاوش داده در واقع فناوری جدید ارائه میدهد تا به ما در آشکار کردن الگوهای پنهان کمک کند، که قابلیت کمک به نوآوری و توسعه نظریات جدید را دارد و اثرات انقلابی بر توسعه نظریه در بسیاری از رشتهها را ارتقا میدهد [4].""]",رشد نمایی حجم دادهها و قدرت محاسباتی در کنار پیشرفت الگوریتمهای یادگیری ماشین و کاوش داده، با کشف الگوهای پنهان در دادههای عظیم، تحولی انقلابی در فرآیند تحقیقات و توسعه نظریات نوین در حوزههای علمی مختلف ایجاد کرده است.
+0,node_2,,"[""طبقه بندی یکی از تکنیک های کاوش داده است."", ""در این روش تمام داده¬ها بر اساس برچسب¬های از قبل تعریف شده طبقه بندی می¬شوند.""]",طبقهبندی یکی از تکنیکهای کاوش داده است که در آن دادهها بر اساس برچسبهای از پیش تعریفشده دستهبندی میشوند.
+0,node_3,,"[""الگوریتم های طبقه بندی به شکل وسیعی در دامنه های مختلف استفاده می¬شوند، از آن جمله برخی از کاربردهایی آنها عبارت اند از پیشگویی ورشکستگی [5]، پیشگویی سرطان [6]، تشخیص چهره [7]، تشخیص کلاه برداری [8]و پیشگویی خرابی نرم افزار [9]."", ""جهت کارایی و عملکرد بهتر در راستای کاربردهایی مذکور بعلاوه¬ی کاربردهای زیادی دیگر، الگوریتم¬هایی طبقه بندی نیاز به مجموعه داده¬های کوچک¬تر و خالص¬تر دارند که با استفاده از روش¬هایی کاهش نمونه روی مجموعه داده¬های اصلی بدست می¬آیند.""]",الگوریتمهای طبقهسازی با وجود کاربردهای گسترده، برای دستیابی به کارایی و عملکرد بهتر نیازمند مجموعهدادههای کوچکتر و خالصتر هستند که از طریق روشهای کاهش نمونه بر روی دادههای اصلی حاصل میشوند.
+1,node_4,,"[""رشد نمایی حجم دادهها و قدرت محاسباتی در کنار پیشرفت الگوریتمهای یادگیری ماشین و کاوش داده، با کشف الگوهای پنهان در دادههای عظیم، تحولی انقلابی در فرآیند تحقیقات و توسعه نظریات نوین در حوزههای علمی مختلف ایجاد کرده است.""]",رشد نمایی دادهها و قدرت محاسباتی به همراه پیشرفت الگوریتمهای یادگیری ماشین، با کشف الگوهای پنهان در دادههای عظیم، تحولی انقلابی در فرآیند تحقیق و توسعه نظریات نوین در حوزههای علمی ایجاد کرده است.
+1,node_5,,"[""طبقهبندی یکی از تکنیکهای کاوش داده است که در آن دادهها بر اساس برچسبهای از پیش تعریفشده دستهبندی میشوند."", ""الگوریتمهای طبقهسازی با وجود کاربردهای گسترده، برای دستیابی به کارایی و عملکرد بهتر نیازمند مجموعهدادههای کوچکتر و خالصتر هستند که از طریق روشهای کاهش نمونه بر روی دادههای اصلی حاصل میشوند.""]",طبقهسازی بهعنوان یکی از تکنیکهای کاوش داده که دادهها را بر اساس برچسبهای از پیش تعریفشده دستهبندی میکند، برای دستیابی به کارایی بهتر نیازمند دادههای خالصتر و کوچکتری است که از طریق روشهای کاهش نمونه حاصل میشوند.
+2,node_6,,"[""رشد نمایی دادهها و قدرت محاسباتی به همراه پیشرفت الگوریتمهای یادگیری ماشین، با کشف الگوهای پنهان در دادههای عظیم، تحولی انقلابی در فرآیند تحقیق و توسعه نظریات نوین در حوزههای علمی ایجاد کرده است."", ""طبقهسازی بهعنوان یکی از تکنیکهای کاوش داده که دادهها را بر اساس برچسبهای از پیش تعریفشده دستهبندی میکند، برای دستیابی به کارایی بهتر نیازمند دادههای خالصتر و کوچکتری است که از طریق روشهای کاهش نمونه حاصل میشوند.""]",رشد نمایی دادهها و پیشرفت الگوریتمهای یادگیری ماشین با کشف الگوهای پنهان، تحولی در تحقیقات علمی ایجاد کرده و طبقهسازی را که برای کارایی بهتر نیازمند دادههای خالصتر و کوچکتر از طریق کاهش نمونه است، بهعنوان یکی از تکنیکهای کلیدی کاوش داده برجسته ساخته است.
diff --git a/projects/2026-04-17_23-20-18/tree_data.csv b/projects/2026-04-17_23-20-18/tree_data.csv
new file mode 100644
index 0000000..1e54c3b
--- /dev/null
+++ b/projects/2026-04-17_23-20-18/tree_data.csv
@@ -0,0 +1,7 @@
+level,segment_id,parent_segment_id,original_sentences,semantic_unit
+0,node_1,,"[""ظهور دادهها و رشد قدرت محاسباتی، به همراه روشهای یادگیری نوین، منجر به دستاوردهای قابل توجهی در بسیاری از حوزههای علمی شده است."", ""در چند سال اخیر، یادگیری ماشینی و هوش مصنوعی یک موج جدید از توجهات را به خود جلب کردهاند که علت اصلی آن، حجم عظیم و روزافزون داده و قدرت محاسباتی، و همچنین کشف الگوریتمهای یادگیری بهبود یافته، می¬باشد[1]."", ""با این حال، ایدهی یادگیری یک رایانه از مجموعه¬ای از مفاهیم انتزاعی و تبدیل داده¬ها به اطلاعات و استفاده از آنها برای پیش بینی، جدید نیست و حداقل از دهه ۱۹۵۰ زمانی که اولین شبکههای عصبی توسعه یافتند، وجود داشته است [2]."", ""همان¬گونه که ذکر شد، حجم زیاد داده¬ها ما را به دوران جدیدی از رشد نمایی در حجم و پیچیدگی دادهها وارد کرده است؛ اینترنت، تلفنهای همراه، کامپیوترها و تجهیزات ضبط باعث ظهور معدن¬های بزرگ داده شده و انسان را به یک تغییر شیب در فرآیند تحقیقات سوق می¬دهد [3]."", ""دانش مدفون در این معدن داده نیازمند فناوریهای کاوش داده است تا الگوهای جالب، معنیدار و قوی را کشف کند."", ""این روش جایگزین تحقیقات نسبت به رویکرد سنتی، اثرات چشمگیری بر همه حوزههای تحقیقات داشته است."", ""کاوش داده در واقع فناوری جدید ارائه میدهد تا به ما در آشکار کردن الگوهای پنهان کمک کند، که قابلیت کمک به نوآوری و توسعه نظریات جدید را دارد و اثرات انقلابی بر توسعه نظریه در بسیاری از رشتهها را ارتقا میدهد [4].""]",رشد نمایی حجم دادهها و قدرت محاسباتی در کنار پیشرفت الگوریتمهای یادگیری ماشین و کاوش داده، با کشف الگوهای پنهان در دادههای بزرگ، انقلابی در نوآوری و توسعه نظریات در حوزههای مختلف علمی ایجاد کرده است.
+0,node_2,,"[""طبقه بندی یکی از تکنیک های کاوش داده است."", ""در این روش تمام داده¬ها بر اساس برچسب¬های از قبل تعریف شده طبقه بندی می¬شوند.""]",طبقهبندی یکی از تکنیکهای کاوش داده است که در آن دادهها بر اساس برچسبهای از پیش تعریفشده دستهبندی میشوند.
+0,node_3,,"[""الگوریتم های طبقه بندی به شکل وسیعی در دامنه های مختلف استفاده می¬شوند، از آن جمله برخی از کاربردهایی آنها عبارت اند از پیشگویی ورشکستگی [5]، پیشگویی سرطان [6]، تشخیص چهره [7]، تشخیص کلاه برداری [8]و پیشگویی خرابی نرم افزار [9]."", ""جهت کارایی و عملکرد بهتر در راستای کاربردهایی مذکور بعلاوه¬ی کاربردهای زیادی دیگر، الگوریتم¬هایی طبقه بندی نیاز به مجموعه داده¬های کوچک¬تر و خالص¬تر دارند که با استفاده از روش¬هایی کاهش نمونه روی مجموعه داده¬های اصلی بدست می¬آیند.""]",الگوریتمهای طبقهسازی که در حوزههای متنوعی از پیشبینی ورشکستگی تا تشخیص چهره کاربرد دارند، برای دستیابی به کارایی و عملکرد بهتر نیازمند مجموعهدادههای کوچکتر و خالصتر هستند که از طریق روشهای کاهش نمونه بر روی دادههای اصلی تولید میشوند.
+1,node_4,,"[""رشد نمایی حجم دادهها و قدرت محاسباتی در کنار پیشرفت الگوریتمهای یادگیری ماشین و کاوش داده، با کشف الگوهای پنهان در دادههای بزرگ، انقلابی در نوآوری و توسعه نظریات در حوزههای مختلف علمی ایجاد کرده است.""]",رشد نمایی دادهها و قدرت محاسباتی در کنار پیشرفت الگوریتمهای یادگیری ماشین و کاوش داده، با کشف الگوهای پنهان، انقلابی در نوآوری و توسعه نظریات علمی ایجاد کرده است.
+1,node_5,,"[""طبقهبندی یکی از تکنیکهای کاوش داده است که در آن دادهها بر اساس برچسبهای از پیش تعریفشده دستهبندی میشوند."", ""الگوریتمهای طبقهسازی که در حوزههای متنوعی از پیشبینی ورشکستگی تا تشخیص چهره کاربرد دارند، برای دستیابی به کارایی و عملکرد بهتر نیازمند مجموعهدادههای کوچکتر و خالصتر هستند که از طریق روشهای کاهش نمونه بر روی دادههای اصلی تولید میشوند.""]",الگوریتمهای طبقهسازی که در حوزههای متنوعی کاربرد دارند، برای دستیابی به کارایی بهتر نیازمند مجموعهدادههای خالصتر و کوچکتری هستند که از طریق روشهای کاهش نمونه بر روی دادههای اصلی تولید میشوند.
+2,node_6,,"[""رشد نمایی دادهها و قدرت محاسباتی در کنار پیشرفت الگوریتمهای یادگیری ماشین و کاوش داده، با کشف الگوهای پنهان، انقلابی در نوآوری و توسعه نظریات علمی ایجاد کرده است."", ""الگوریتمهای طبقهسازی که در حوزههای متنوعی کاربرد دارند، برای دستیابی به کارایی بهتر نیازمند مجموعهدادههای خالصتر و کوچکتری هستند که از طریق روشهای کاهش نمونه بر روی دادههای اصلی تولید میشوند.""]",رشد نمایی دادهها و قدرت محاسباتی همراه با پیشرفت الگوریتمها، انقلابی در نوآوری علمی ایجاد کرده و الگوریتمهای طبقهسازی برای دستیابی به کارایی بهتر نیازمند دادههای خالصتر و کوچکتری هستند که از طریق روشهای کاهش نمونه تولید میشوند.
diff --git a/projects/2026-04-17_23-20-18/tree_visualization.html b/projects/2026-04-17_23-20-18/tree_visualization.html
new file mode 100644
index 0000000..e69de29
diff --git a/projects/2026-04-17_23-25-21/tree_data.csv b/projects/2026-04-17_23-25-21/tree_data.csv
new file mode 100644
index 0000000..72083c4
--- /dev/null
+++ b/projects/2026-04-17_23-25-21/tree_data.csv
@@ -0,0 +1,7 @@
+level,segment_id,parent_segment_id,original_sentences,semantic_unit
+0,node_1,,"[""\""\""\""\""ظهور دادهها و رشد قدرت محاسباتی، به همراه روشهای یادگیری نوین، منجر به دستاوردهای قابل توجهی در بسیاری از حوزههای علمی شده است."", ""در چند سال اخیر، یادگیری ماشینی و هوش مصنوعی یک موج جدید از توجهات را به خود جلب کردهاند که علت اصلی آن، حجم عظیم و روزافزون داده و قدرت محاسباتی، و همچنین کشف الگوریتمهای یادگیری بهبود یافته، می¬باشد[1]."", ""با این حال، ایدهی یادگیری یک رایانه از مجموعه¬ای از مفاهیم انتزاعی و تبدیل داده¬ها به اطلاعات و استفاده از آنها برای پیش بینی، جدید نیست و حداقل از دهه ۱۹۵۰ زمانی که اولین شبکههای عصبی توسعه یافتند، وجود داشته است [2]."", ""همان¬گونه که ذکر شد، حجم زیاد داده¬ها ما را به دوران جدیدی از رشد نمایی در حجم و پیچیدگی دادهها وارد کرده است؛ اینترنت، تلفنهای همراه، کامپیوترها و تجهیزات ضبط باعث ظهور معدن¬های بزرگ داده شده و انسان را به یک تغییر شیب در فرآیند تحقیقات سوق می¬دهد [3]."", ""دانش مدفون در این معدن داده نیازمند فناوریهای کاوش داده است تا الگوهای جالب، معنیدار و قوی را کشف کند."", ""این روش جایگزین تحقیقات نسبت به رویکرد سنتی، اثرات چشمگیری بر همه حوزههای تحقیقات داشته است."", ""کاوش داده در واقع فناوری جدید ارائه میدهد تا به ما در آشکار کردن الگوهای پنهان کمک کند، که قابلیت کمک به نوآوری و توسعه نظریات جدید را دارد و اثرات انقلابی بر توسعه نظریه در بسیاری از رشتهها را ارتقا میدهد [4].""]",رشد نمایی حجم دادهها و قدرت محاسباتی به همراه پیشرفت الگوریتمهای یادگیری ماشین و کاوش داده، با آشکار کردن الگوهای پنهان در این دادهها، موج جدیدی از نوآوری و تحول انقلابی را در فرآیند تحقیقات و توسعه نظریات در حوزههای علمی مختلف ایجاد کرده است.
+0,node_2,,"[""طبقه بندی یکی از تکنیک های کاوش داده است."", ""در این روش تمام داده¬ها بر اساس برچسب¬های از قبل تعریف شده طبقه بندی می¬شوند.""]",طبقهبندی یکی از تکنیکهای کاوش داده است که در آن دادهها بر اساس برچسبهای از پیش تعریفشده دستهبندی میشوند.
+0,node_3,,"[""الگوریتم های طبقه بندی به شکل وسیعی در دامنه های مختلف استفاده می¬شوند، از آن جمله برخی از کاربردهایی آنها عبارت اند از پیشگویی ورشکستگی [5]، پیشگویی سرطان [6]، تشخیص چهره [7]، تشخیص کلاه برداری [8]و پیشگویی خرابی نرم افزار [9]."", ""جهت کارایی و عملکرد بهتر در راستای کاربردهایی مذکور بعلاوه¬ی کاربردهای زیادی دیگر، الگوریتم¬هایی طبقه بندی نیاز به مجموعه داده¬های کوچک¬تر و خالص¬تر دارند که با استفاده از روش¬هایی کاهش نمونه روی مجموعه داده¬های اصلی بدست می¬آیند.\r \""\""\""""]",الگوریتمهای طبقهسازی با وجود کاربرد گسترده در حوزههایی مانند پیشبینی ورشکستگی، سرطان، تشخیص چهره و کلاهبرداری، برای دستیابی به عملکرد بهینه نیازمند مجموعهدادههای کوچکتر و خالصتر هستند که از طریق روشهای کاهش نمونه حاصل میشوند.
+1,node_4,,"[""رشد نمایی حجم دادهها و قدرت محاسباتی به همراه پیشرفت الگوریتمهای یادگیری ماشین و کاوش داده، با آشکار کردن الگوهای پنهان در این دادهها، موج جدیدی از نوآوری و تحول انقلابی را در فرآیند تحقیقات و توسعه نظریات در حوزههای علمی مختلف ایجاد کرده است.""]",رشد نمایی دادهها و قدرت محاسباتی به همراه پیشرفت الگوریتمهای یادگیری ماشین، با کشف الگوهای پنهان، موجی از نوآوری و تحول انقلابی را در فرآیند تحقیق و توسعه نظریات علمی ایجاد کرده است.
+1,node_5,,"[""طبقهبندی یکی از تکنیکهای کاوش داده است که در آن دادهها بر اساس برچسبهای از پیش تعریفشده دستهبندی میشوند."", ""الگوریتمهای طبقهسازی با وجود کاربرد گسترده در حوزههایی مانند پیشبینی ورشکستگی، سرطان، تشخیص چهره و کلاهبرداری، برای دستیابی به عملکرد بهینه نیازمند مجموعهدادههای کوچکتر و خالصتر هستند که از طریق روشهای کاهش نمونه حاصل میشوند.""]",طبقهبندی به عنوان یکی از تکنیکهای کاوش داده که دادهها را بر اساس برچسبهای از پیش تعریفشده دستهبندی میکند، برای دستیابی به عملکرد بهینه در کاربردهای گستردهای همچون پیشبینی و تشخیص، نیازمند دادههای خالصتر و کوچکتر حاصل از روشهای کاهش نمونه است.
+2,node_6,,"[""رشد نمایی دادهها و قدرت محاسباتی به همراه پیشرفت الگوریتمهای یادگیری ماشین، با کشف الگوهای پنهان، موجی از نوآوری و تحول انقلابی را در فرآیند تحقیق و توسعه نظریات علمی ایجاد کرده است."", ""طبقهبندی به عنوان یکی از تکنیکهای کاوش داده که دادهها را بر اساس برچسبهای از پیش تعریفشده دستهبندی میکند، برای دستیابی به عملکرد بهینه در کاربردهای گستردهای همچون پیشبینی و تشخیص، نیازمند دادههای خالصتر و کوچکتر حاصل از روشهای کاهش نمونه است.""]",رشد نمایی دادهها و پیشرفت الگوریتمهای یادگیری ماشین با کشف الگوهای پنهان تحولی در تحقیقات علمی ایجاد کرده و طبقهبندی برای دستیابی به عملکرد بهینه در کاربردهای گسترده نیازمند دادههای خالصتر و کوچکتر حاصل از روشهای کاهش نمونه است.
diff --git a/projects/2026-04-17_23-25-21/tree_visualization.html b/projects/2026-04-17_23-25-21/tree_visualization.html
new file mode 100644
index 0000000..d1ee255
--- /dev/null
+++ b/projects/2026-04-17_23-25-21/tree_visualization.html
@@ -0,0 +1,341 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+