240 lines
9.5 KiB
Python
240 lines
9.5 KiB
Python
|
|
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/<project_name>')
|
||
|
|
def show_tree(project_name):
|
||
|
|
"""نمایش صفحه وب حاوی درخت سلسلهمراتبی"""
|
||
|
|
return render_template('tree_view.html', project_name=project_name)
|
||
|
|
|
||
|
|
|
||
|
|
@app.route('/tree_iframe/<project_name>')
|
||
|
|
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/<project_name>/<node_id>')
|
||
|
|
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/<project_name>/<node_id>')
|
||
|
|
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)
|