Hoghoghi / app /app.py
Really-amin's picture
Update app/app.py
784d7e4 verified
raw
history blame
67.1 kB
"""
پلتفرم پیشرفته هوشمند اسناد حقوقی ایران - نسخه ارتقاء یافته
مجهز به مدل‌های SOTA فارسی، سیستم کش هوشمند و امتیازدهی پیشرفته
"""
import os
import gc
import sys
import time
import json
import logging
import hashlib
import resource
import requests
import threading
import re
import random
import sqlite3
import pickle
import tempfile
from pathlib import Path
from datetime import datetime, timedelta
from typing import List, Dict, Any, Optional, Tuple, Union
from dataclasses import dataclass, field
from concurrent.futures import ThreadPoolExecutor, as_completed, ProcessPoolExecutor
from urllib.parse import urljoin, urlparse
import warnings
import asyncio
from functools import lru_cache
import numpy as np
import gradio as gr
import pandas as pd
import torch
from bs4 import BeautifulSoup
from transformers import (
AutoTokenizer,
AutoModelForSequenceClassification,
pipeline,
logging as transformers_logging,
AutoModel
)
from sentence_transformers import SentenceTransformer, util
from hazm import Normalizer, word_tokenize, Lemmatizer
import faiss
# === تنظیمات اولیه پیشرفته ===
warnings.filterwarnings('ignore')
transformers_logging.set_verbosity_error()
try:
resource.setrlimit(resource.RLIMIT_AS, (4*1024*1024*1024, 4*1024*1024*1024))
except:
pass
# ایجاد دایرکتوری‌های مورد نیاز
WORK_DIR = Path("/tmp/legal_scraper_data")
WORK_DIR.mkdir(exist_ok=True, parents=True)
os.environ.update({
'TRANSFORMERS_CACHE': str(WORK_DIR / 'hf_cache'),
'HF_HOME': str(WORK_DIR / 'hf_cache'),
'TORCH_HOME': str(WORK_DIR / 'torch_cache'),
'TOKENIZERS_PARALLELISM': 'false',
'CUDA_VISIBLE_DEVICES': '0' if torch.cuda.is_available() else ''
})
# ایجاد دایرکتوری‌های کش
for cache_dir in [os.environ['TRANSFORMERS_CACHE'], os.environ['HF_HOME'], os.environ['TORCH_HOME']]:
os.makedirs(cache_dir, exist_ok=True)
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# === ثابت‌های سیستم با مسیرهای اصلاح شده ===
DB_PATH = str(WORK_DIR / "iranian_legal_archive_advanced.sqlite")
CACHE_DB_PATH = str(WORK_DIR / "cache_system.sqlite")
EMBEDDINGS_CACHE_PATH = str(WORK_DIR / "embeddings_cache.pkl")
VECTOR_INDEX_PATH = str(WORK_DIR / "faiss_index.bin")
# آیکون‌های بهبود یافته
SVG_ICONS = {
'search': '🔍', 'document': '📄', 'analyze': '🤖', 'export': '📊',
'settings': '⚙️', 'link': '🔗', 'success': '✅', 'error': '❌',
'warning': '⚠️', 'database': '🗄️', 'crawler': '🔄', 'brain': '🧠',
'cache': '⚡', 'score': '📈', 'classify': '🏷️', 'similarity': '🎯',
'legal': '⚖️', 'home': '🏠', 'stats': '📈', 'process': '🔧'
}
# منابع حقوقی با پیکربندی پیشرفته
LEGAL_SOURCES = {
"مجلس شورای اسلامی": {
"base_url": "https://rc.majlis.ir",
"patterns": ["/fa/law/show/", "/fa/report/show/"],
"selectors": [".main-content", ".article-body", "article"],
"delay_range": (2, 5),
"priority": 1,
"reliability_score": 0.95
},
"پورتال ملی قوانین": {
"base_url": "https://www.dotic.ir",
"patterns": ["/portal/law/", "/regulation/"],
"selectors": [".content-area", ".law-content"],
"delay_range": (1, 4),
"priority": 1,
"reliability_score": 0.90
},
"قوه قضاییه": {
"base_url": "https://www.judiciary.ir",
"patterns": ["/fa/news/", "/fa/verdict/"],
"selectors": [".news-content", ".main-content"],
"delay_range": (3, 6),
"priority": 2,
"reliability_score": 0.85
},
"وزارت دادگستری": {
"base_url": "https://www.moj.ir",
"patterns": ["/fa/news/", "/fa/regulation/"],
"selectors": [".content-area", ".news-content"],
"delay_range": (2, 4),
"priority": 2,
"reliability_score": 0.80
}
}
# واژگان تخصصی حقوقی گسترده
PERSIAN_LEGAL_TERMS = {
"قوانین_اساسی": ["قانون اساسی", "مجلس شورای اسلامی", "شورای نگهبان", "ولایت فقیه", "اصول قانون اساسی"],
"قوانین_عادی": ["ماده", "تبصره", "فصل", "باب", "قانون مدنی", "قانون جزا", "قانون تجارت", "قانون کار"],
"اصطلاحات_حقوقی": ["شخص حقیقی", "شخص حقوقی", "دعوا", "خواهان", "خوانده", "مجازات", "قرارداد", "تعهد"],
"نهادهای_قضایی": ["دادگاه", "قاضی", "وکیل", "مدعی‌العموم", "رای", "حکم", "دادنامه", "قرار"],
"اصطلاحات_مالی": ["مالیات", "عوارض", "جریمه", "خسارت", "تأمین", "ضمانت", "وثیقه", "دیه"],
"جرائم": ["جنایت", "جنحه", "تخلف", "قصاص", "دیه", "تعزیر", "حدود", "قذف"]
}
# مدل‌های SOTA پیشنهادی
AVAILABLE_MODELS = {
"classification": {
"fabert": "sbunlp/fabert",
"parsbert": "HooshvareLab/bert-base-parsbert-uncased",
"legal_roberta": "lexlms/legal-roberta-base"
},
"embedding": {
"maux_gte": "xmanii/maux-gte-persian",
"sentence_transformer": "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
},
"ner": {
"parsbert_ner": "HooshvareLab/bert-fa-base-uncased-ner"
}
}
@dataclass
class ProcessingResult:
"""نتیجه پردازش سند"""
url: str
title: str
content: str
source: str
quality_score: float
classification: Dict[str, float]
sentiment_score: float
legal_entities: List[str]
embeddings: Optional[np.ndarray]
processing_time: float
cache_hit: bool = False
@dataclass
class SystemMetrics:
"""متریک‌های عملکرد سیستم"""
total_processed: int = 0
cache_hits: int = 0
classification_accuracy: float = 0.0
avg_processing_time: float = 0.0
memory_usage: float = 0.0
active_crawlers: int = 0
# === سیستم کش هوشمند ===
class IntelligentCacheSystem:
"""سیستم کش هوشمند با TTL و اولویت‌بندی"""
def __init__(self, cache_db_path: str = CACHE_DB_PATH):
self.cache_db_path = cache_db_path
self.memory_cache = {}
self.access_count = {}
self.max_memory_items = 1000
self._init_database()
def _init_database(self):
"""ایجاد پایگاه داده کش"""
try:
with sqlite3.connect(self.cache_db_path) as conn:
conn.execute('''
CREATE TABLE IF NOT EXISTS cache_entries (
key TEXT PRIMARY KEY,
value BLOB,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
access_count INTEGER DEFAULT 1,
ttl_seconds INTEGER DEFAULT 3600,
priority INTEGER DEFAULT 1
)
''')
conn.execute('''
CREATE INDEX IF NOT EXISTS idx_created_ttl ON cache_entries(created_at, ttl_seconds)
''')
except Exception as e:
logger.error(f"خطا در ایجاد پایگاه داده کش: {e}")
def _generate_key(self, url: str, model_type: str = "general") -> str:
"""تولید کلید یکتا برای کش"""
content = f"{url}:{model_type}"
return hashlib.md5(content.encode()).hexdigest()
def get(self, url: str, model_type: str = "general") -> Optional[Dict]:
"""دریافت از کش با بررسی TTL"""
key = self._generate_key(url, model_type)
# بررسی memory cache
if key in self.memory_cache:
self.access_count[key] = self.access_count.get(key, 0) + 1
return self.memory_cache[key]
# بررسی database cache
try:
with sqlite3.connect(self.cache_db_path) as conn:
cursor = conn.execute('''
SELECT value, created_at, ttl_seconds, access_count
FROM cache_entries
WHERE key = ?
''', (key,))
row = cursor.fetchone()
if row:
value_blob, created_at, ttl_seconds, access_count = row
created_time = datetime.fromisoformat(created_at)
# بررسی انقضاء
if datetime.now() - created_time < timedelta(seconds=ttl_seconds):
# بروزرسانی شمارنده دسترسی
conn.execute(
'UPDATE cache_entries SET access_count = access_count + 1 WHERE key = ?',
(key,)
)
# اضافه به memory cache
value = pickle.loads(value_blob)
self._add_to_memory_cache(key, value)
return value
else:
# حذف entry منقضی
conn.execute('DELETE FROM cache_entries WHERE key = ?', (key,))
except Exception as e:
logger.error(f"خطا در دریافت از کش: {e}")
return None
def set(self, url: str, value: Dict, model_type: str = "general",
ttl_seconds: int = 3600, priority: int = 1):
"""ذخیره در کش"""
key = self._generate_key(url, model_type)
# ذخیره در memory cache
self._add_to_memory_cache(key, value)
# ذخیره در database
try:
with sqlite3.connect(self.cache_db_path) as conn:
value_blob = pickle.dumps(value)
conn.execute('''
INSERT OR REPLACE INTO cache_entries
(key, value, ttl_seconds, priority)
VALUES (?, ?, ?, ?)
''', (key, value_blob, ttl_seconds, priority))
except Exception as e:
logger.error(f"خطا در ذخیره در کش: {e}")
def _add_to_memory_cache(self, key: str, value: Dict):
"""اضافه کردن به memory cache با مدیریت حد"""
if len(self.memory_cache) >= self.max_memory_items:
# حذف کم‌استفاده‌ترین item
if self.access_count:
least_used_key = min(self.access_count.keys(), key=self.access_count.get)
if least_used_key in self.memory_cache:
del self.memory_cache[least_used_key]
if least_used_key in self.access_count:
del self.access_count[least_used_key]
self.memory_cache[key] = value
self.access_count[key] = self.access_count.get(key, 0) + 1
def cleanup_expired(self):
"""پاکسازی entries منقضی"""
try:
with sqlite3.connect(self.cache_db_path) as conn:
conn.execute('''
DELETE FROM cache_entries
WHERE datetime(created_at, '+' || ttl_seconds || ' seconds') < datetime('now')
''')
conn.commit()
except Exception as e:
logger.error(f"خطا در پاکسازی کش: {e}")
def get_stats(self) -> Dict:
"""آمار کش"""
try:
with sqlite3.connect(self.cache_db_path) as conn:
cursor = conn.execute('''
SELECT
COUNT(*) as total_entries,
SUM(access_count) as total_accesses,
AVG(access_count) as avg_accesses
FROM cache_entries
''')
stats = cursor.fetchone()
return {
'memory_cache_size': len(self.memory_cache),
'database_entries': stats[0] if stats and stats[0] else 0,
'total_accesses': stats[1] if stats and stats[1] else 0,
'average_accesses': round(stats[2], 2) if stats and stats[2] else 0
}
except Exception as e:
logger.error(f"خطا در دریافت آمار کش: {e}")
return {
'memory_cache_size': len(self.memory_cache),
'database_entries': 0,
'total_accesses': 0,
'average_accesses': 0
}
# === سیستم امتیازدهی پیشرفته ===
class AdvancedScoringSystem:
"""سیستم امتیازدهی پیشرفته با وزن‌دهی چندگانه"""
def __init__(self):
self.weights = {
'content_length': 0.15,
'legal_terms_density': 0.25,
'source_reliability': 0.20,
'structure_quality': 0.15,
'linguistic_quality': 0.15,
'citation_count': 0.10
}
try:
self.normalizer = Normalizer()
self.lemmatizer = Lemmatizer()
except Exception as e:
logger.warning(f"خطا در بارگذاری ابزارهای پردازش متن: {e}")
self.normalizer = None
self.lemmatizer = None
def calculate_comprehensive_score(self, content: str, source_info: Dict,
legal_entities: List[str]) -> Dict[str, float]:
"""محاسبه امتیاز جامع"""
scores = {}
try:
# امتیاز طول محتوا
scores['content_length'] = self._score_content_length(content)
# تراکم اصطلاحات حقوقی
scores['legal_terms_density'] = self._score_legal_terms_density(content)
# قابلیت اعتماد منبع
scores['source_reliability'] = source_info.get('reliability_score', 0.5)
# کیفیت ساختار
scores['structure_quality'] = self._score_structure_quality(content)
# کیفیت زبانی
scores['linguistic_quality'] = self._score_linguistic_quality(content)
# تعداد ارجاعات
scores['citation_count'] = self._score_citations(content, legal_entities)
# محاسبه امتیاز نهایی
final_score = sum(
scores[factor] * self.weights[factor]
for factor in scores
)
scores['final_score'] = min(100, max(0, final_score * 100))
except Exception as e:
logger.error(f"خطا در محاسبه امتیاز: {e}")
scores = {
'content_length': 0.5,
'legal_terms_density': 0.5,
'source_reliability': 0.5,
'structure_quality': 0.5,
'linguistic_quality': 0.5,
'citation_count': 0.5,
'final_score': 50.0
}
return scores
def _score_content_length(self, content: str) -> float:
"""امتیازدهی بر اساس طول محتوا"""
try:
word_count = len(content.split())
if word_count < 50:
return word_count / 50 * 0.5
elif word_count > 2000:
return 1.0
else:
return 0.5 + (word_count - 50) / 1950 * 0.5
except:
return 0.5
def _score_legal_terms_density(self, content: str) -> float:
"""امتیازدهی تراکم اصطلاحات حقوقی"""
try:
total_terms = 0
content_lower = content.lower()
for category, terms in PERSIAN_LEGAL_TERMS.items():
for term in terms:
total_terms += content_lower.count(term.lower())
words = len(content.split())
if words == 0:
return 0.0
density = total_terms / words
return min(1.0, density * 20) # نرمال‌سازی
except:
return 0.5
def _score_structure_quality(self, content: str) -> float:
"""امتیازدهی کیفیت ساختار"""
try:
score = 0.0
# بررسی وجود ساختار مواد و تبصره‌ها
if 'ماده' in content:
score += 0.3
if 'تبصره' in content:
score += 0.2
if 'فصل' in content or 'باب' in content:
score += 0.2
# بررسی پاراگراف‌بندی
paragraphs = content.split('\n')
if len(paragraphs) > 2:
score += 0.3
return min(1.0, score)
except:
return 0.5
def _score_linguistic_quality(self, content: str) -> float:
"""امتیازدهی کیفیت زبانی"""
try:
score = 0.0
if not content:
return 0.0
# نسبت کاراکترهای فارسی
persian_chars = sum(1 for c in content if '\u0600' <= c <= '\u06FF')
persian_ratio = persian_chars / len(content)
score += persian_ratio * 0.5
# بررسی وجود علائم نگارشی
punctuation_count = sum(1 for c in content if c in '.,;:!؟')
words_count = len(content.split())
if words_count > 0:
punctuation_ratio = punctuation_count / words_count
score += min(0.3, punctuation_ratio * 3)
# بررسی طول متوسط جملات
sentences = re.split(r'[.؟!]', content)
if sentences:
avg_sentence_length = sum(len(s.split()) for s in sentences) / len(sentences)
if 10 <= avg_sentence_length <= 25:
score += 0.2
return min(1.0, score)
except:
return 0.5
def _score_citations(self, content: str, legal_entities: List[str]) -> float:
"""امتیازدهی ارجاعات قانونی"""
try:
citation_patterns = [
r'ماده\s*\d+', r'تبصره\s*\d+', r'بند\s*\d+',
r'فصل\s*\d+', r'قانون\s+[آ-ی\s]+', r'مصوبه\s+[آ-ی\s]+'
]
total_citations = 0
for pattern in citation_patterns:
total_citations += len(re.findall(pattern, content))
# اضافه کردن موجودیت‌های قانونی
total_citations += len(legal_entities)
# نرمال‌سازی (هر 5 ارجاع = امتیاز کامل)
return min(1.0, total_citations / 5)
except:
return 0.5
# === سیستم طبقه‌بندی هوشمند ===
class IntelligentClassificationSystem:
"""سیستم طبقه‌بندی هوشمند چندمرحله‌ای"""
def __init__(self, cache_system: IntelligentCacheSystem):
self.cache_system = cache_system
self.models = {}
self.is_ready = False
# دسته‌های حقوقی
self.legal_categories = {
'قانون': ['قانون', 'مقررات', 'آیین‌نامه'],
'دادنامه': ['دادنامه', 'رای', 'حکم'],
'قرارداد': ['قرارداد', 'توافق‌نامه', 'پروتکل'],
'لایحه': ['لایحه', 'طرح', 'پیشنهاد'],
'بخشنامه': ['بخشنامه', 'دستورالعمل', 'رهنمود'],
'نظریه': ['نظریه', 'استعلام', 'پاسخ']
}
def load_models(self):
"""بارگذاری مدل‌های طبقه‌بندی"""
try:
logger.info("شروع بارگذاری مدل‌ها...")
# بارگذاری ساده مدل embedding
try:
self.models['embedder'] = SentenceTransformer(
AVAILABLE_MODELS['embedding']['sentence_transformer']
)
logger.info("مدل embedding بارگذاری شد")
except Exception as e:
logger.warning(f"خطا در بارگذاری مدل embedding: {e}")
self.is_ready = True
logger.info("سیستم طبقه‌بندی آماده است")
except Exception as e:
logger.error(f"خطا در بارگذاری مدل‌ها: {e}")
self.is_ready = False
def classify_document(self, content: str, use_cache: bool = True) -> Dict:
"""طبقه‌بندی هوشمند سند"""
if not content or not content.strip():
return {'error': 'محتوا خالی است'}
# بررسی کش
cache_key = hashlib.md5(content.encode()).hexdigest()
if use_cache:
cached = self.cache_system.get(cache_key, 'classification')
if cached:
cached['cache_hit'] = True
return cached
start_time = time.time()
result = {}
try:
# متن نمونه برای پردازش
text_sample = ' '.join(content.split()[:400])
# طبقه‌بندی بر اساس قوانین
result['rule_based_classification'] = self._rule_based_classify(content)
# استخراج موجودیت‌های حقوقی
result['legal_entities'] = self._extract_legal_entities(content)
# تولید embedding
if 'embedder' in self.models:
try:
result['embedding'] = self.models['embedder'].encode(text_sample)
except Exception as e:
logger.warning(f"خطا در تولید embedding: {e}")
result['embedding'] = None
# محاسبه اعتماد ترکیبی
result['confidence_score'] = self._calculate_combined_confidence(result)
result['processing_time'] = time.time() - start_time
result['cache_hit'] = False
# ذخیره در کش
if use_cache:
self.cache_system.set(cache_key, result, 'classification', ttl_seconds=7200)
except Exception as e:
logger.error(f"خطا در طبقه‌بندی: {e}")
result['error'] = str(e)
return result
def _rule_based_classify(self, content: str) -> Dict[str, float]:
"""طبقه‌بندی بر اساس قوانین"""
scores = {}
content_lower = content.lower()
for category, keywords in self.legal_categories.items():
score = 0.0
for keyword in keywords:
count = content_lower.count(keyword.lower())
score += count * 0.1
scores[category] = min(1.0, score)
# نرمال‌سازی امتیازها
total_score = sum(scores.values())
if total_score > 0:
scores = {k: v/total_score for k, v in scores.items()}
return scores
def _extract_legal_entities(self, content: str) -> List[str]:
"""استخراج موجودیت‌های حقوقی"""
entities = []
try:
# الگوهای ارجاعات قانونی
patterns = [
r'ماده\s*(\d+)', r'تبصره\s*(\d+)', r'بند\s*([الف-ی]|\d+)',
r'فصل\s*(\d+)', r'قانون\s+([آ-ی\s]{5,50})',
r'مصوبه\s+([آ-ی\s]{5,50})'
]
for pattern in patterns:
matches = re.findall(pattern, content)
entities.extend([str(m) for m in matches])
except Exception as e:
logger.error(f"خطا در استخراج موجودیت‌ها: {e}")
return list(set(entities))[:20]
def _calculate_combined_confidence(self, result: Dict) -> float:
"""محاسبه اعتماد ترکیبی"""
try:
confidence = 0.0
weights = {'rule_based': 0.7, 'entities': 0.3}
# اعتماد طبقه‌بندی قانونی
if 'rule_based_classification' in result:
rule_conf = max(result['rule_based_classification'].values()) if result['rule_based_classification'] else 0
confidence += rule_conf * weights['rule_based']
# تعداد موجودیت‌های قانونی
entity_count = len(result.get('legal_entities', []))
entity_conf = min(1.0, entity_count / 5)
confidence += entity_conf * weights['entities']
return round(confidence, 3)
except:
return 0.5
# === مدیریت پایگاه داده پیشرفته ===
class AdvancedDatabaseManager:
"""مدیریت پیشرفته پایگاه داده با بهینه‌سازی‌های جدید"""
def __init__(self, db_path: str = DB_PATH):
self.db_path = db_path
self._init_database()
def _init_database(self):
"""ایجاد پایگاه داده با جداول پیشرفته"""
try:
with sqlite3.connect(self.db_path) as conn:
# جدول اسناد اصلی
conn.execute('''
CREATE TABLE IF NOT EXISTS documents (
id INTEGER PRIMARY KEY AUTOINCREMENT,
url TEXT UNIQUE NOT NULL,
title TEXT,
source TEXT,
content TEXT,
word_count INTEGER,
quality_scores TEXT,
classification_result TEXT,
legal_entities TEXT,
embedding_vector BLOB,
scraped_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
processing_status TEXT DEFAULT 'pending'
)
''')
# ایجاد ایندکس‌ها برای جدول documents
conn.execute('CREATE INDEX IF NOT EXISTS idx_documents_source ON documents(source)')
conn.execute('CREATE INDEX IF NOT EXISTS idx_documents_scraped_at ON documents(scraped_at)')
conn.execute('CREATE INDEX IF NOT EXISTS idx_documents_status ON documents(processing_status)')
conn.execute('CREATE INDEX IF NOT EXISTS idx_documents_updated ON documents(last_updated)')
# جدول متریک‌های عملکرد
conn.execute('''
CREATE TABLE IF NOT EXISTS performance_metrics (
id INTEGER PRIMARY KEY AUTOINCREMENT,
metric_name TEXT,
metric_value REAL,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
# ایندکس‌ها برای جدول metrics
conn.execute('CREATE INDEX IF NOT EXISTS idx_metrics_name ON performance_metrics(metric_name)')
conn.execute('CREATE INDEX IF NOT EXISTS idx_metrics_timestamp ON performance_metrics(timestamp)')
# جدول لاگ‌های پردازش
conn.execute('''
CREATE TABLE IF NOT EXISTS processing_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
document_id INTEGER,
operation TEXT,
status TEXT,
details TEXT,
processing_time REAL,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (document_id) REFERENCES documents (id)
)
''')
# ایندکس‌ها برای جدول logs
conn.execute('CREATE INDEX IF NOT EXISTS idx_logs_operation ON processing_logs(operation)')
conn.execute('CREATE INDEX IF NOT EXISTS idx_logs_status ON processing_logs(status)')
conn.execute('CREATE INDEX IF NOT EXISTS idx_logs_timestamp ON processing_logs(timestamp)')
conn.execute('CREATE INDEX IF NOT EXISTS idx_logs_document_id ON processing_logs(document_id)')
except Exception as e:
logger.error(f"خطا در ایجاد پایگاه داده: {e}")
def save_document_advanced(self, result: ProcessingResult) -> bool:
"""ذخیره پیشرفته سند"""
try:
with sqlite3.connect(self.db_path) as conn:
# محاسبه embedding bytes
embedding_blob = None
if result.embeddings is not None:
embedding_blob = result.embeddings.tobytes()
conn.execute('''
INSERT OR REPLACE INTO documents
(url, title, source, content, word_count, quality_scores,
classification_result, legal_entities, embedding_vector,
processing_status, last_updated)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'completed', datetime('now'))
''', (
result.url,
result.title,
result.source,
result.content,
len(result.content.split()),
json.dumps({'quality_score': result.quality_score}, ensure_ascii=False),
json.dumps(result.classification, ensure_ascii=False),
json.dumps(result.legal_entities, ensure_ascii=False),
embedding_blob
))
# ثبت لاگ
document_id = conn.lastrowid
conn.execute('''
INSERT INTO processing_logs
(document_id, operation, status, processing_time)
VALUES (?, 'save', 'success', ?)
''', (document_id, result.processing_time))
return True
except Exception as e:
logger.error(f"خطا در ذخیره پیشرفته: {e}")
return False
def get_documents_with_embeddings(self) -> List[Tuple]:
"""دریافت اسناد همراه با embeddings"""
try:
with sqlite3.connect(self.db_path) as conn:
return conn.execute('''
SELECT id, url, title, content, embedding_vector
FROM documents
WHERE processing_status = 'completed'
AND content IS NOT NULL
ORDER BY last_updated DESC
''').fetchall()
except Exception as e:
logger.error(f"خطا در دریافت اسناد: {e}")
return []
def get_advanced_stats(self) -> Dict:
"""آمار پیشرفته سیستم"""
try:
with sqlite3.connect(self.db_path) as conn:
# آمار کلی
total_docs = conn.execute("SELECT COUNT(*) FROM documents").fetchone()[0]
# آمار بر اساس منبع
source_stats = conn.execute('''
SELECT source, COUNT(*), AVG(word_count)
FROM documents
GROUP BY source
ORDER BY COUNT(*) DESC
''').fetchall()
# آمار عملکرد
avg_processing_time = conn.execute('''
SELECT AVG(processing_time)
FROM processing_logs
WHERE operation = 'save' AND status = 'success'
''').fetchone()[0] or 0
# آمار وضعیت پردازش
status_stats = conn.execute('''
SELECT processing_status, COUNT(*)
FROM documents
GROUP BY processing_status
''').fetchall()
return {
'total_documents': total_docs,
'source_statistics': source_stats,
'average_processing_time': round(avg_processing_time, 2),
'status_statistics': status_stats,
'last_updated': datetime.now().isoformat()
}
except Exception as e:
logger.error(f"خطا در دریافت آمار: {e}")
return {
'total_documents': 0,
'source_statistics': [],
'average_processing_time': 0,
'status_statistics': [],
'last_updated': datetime.now().isoformat()
}
# === سیستم جستجوی معنایی پیشرفته ===
class SemanticSearchEngine:
"""موتور جستجوی معنایی با ایندکس FAISS"""
def __init__(self, cache_system: IntelligentCacheSystem):
self.cache_system = cache_system
self.embedder = None
self.faiss_index = None
self.documents = []
self.document_embeddings = None
self.is_ready = False
def initialize(self):
"""مقداردهی اولیه موتور جستجو"""
try:
self.embedder = SentenceTransformer(
AVAILABLE_MODELS['embedding']['sentence_transformer']
)
logger.info("مدل embedding بارگذاری شد")
# بارگذاری ایندکس موجود در صورت وجود
if os.path.exists(VECTOR_INDEX_PATH) and os.path.exists(EMBEDDINGS_CACHE_PATH):
self._load_existing_index()
self.is_ready = True
except Exception as e:
logger.error(f"خطا در مقداردهی موتور جستجو: {e}")
def build_index(self, documents: List[Tuple], progress_callback=None):
"""ساخت ایندکس FAISS"""
try:
if not documents:
return False
if progress_callback:
progress_callback("استخراج متون...", 0.1)
self.documents = documents
texts = [doc[3] for doc in documents if doc[3]] # content field
if progress_callback:
progress_callback(f"تولید embedding برای {len(texts)} سند...", 0.3)
# تولید embeddings با batch processing
batch_size = 16
all_embeddings = []
for i in range(0, len(texts), batch_size):
batch = texts[i:i+batch_size]
batch_embeddings = self.embedder.encode(
batch,
convert_to_tensor=True,
show_progress_bar=False
)
all_embeddings.append(batch_embeddings)
if progress_callback:
progress = 0.3 + (i / len(texts)) * 0.6
progress_callback(f"پردازش batch {i//batch_size + 1}...", progress)
# ترکیب embeddings
self.document_embeddings = torch.cat(all_embeddings, dim=0).cpu().numpy()
if progress_callback:
progress_callback("ساخت ایندکس FAISS...", 0.9)
# ساخت ایندکس FAISS
dimension = self.document_embeddings.shape[1]
self.faiss_index = faiss.IndexFlatIP(dimension) # Inner Product for cosine similarity
# نرمال‌سازی برای cosine similarity
faiss.normalize_L2(self.document_embeddings)
self.faiss_index.add(self.document_embeddings)
# ذخیره ایندکس
self._save_index()
if progress_callback:
progress_callback("تکمیل ایندکس‌سازی", 1.0)
logger.info(f"ایندکس با {len(documents)} سند آماده شد")
return True
except Exception as e:
logger.error(f"خطا در ساخت ایندکس: {e}")
return False
def search(self, query: str, top_k: int = 10, threshold: float = 0.3) -> List[Dict]:
"""جستجوی معنایی پیشرفته"""
if not self.is_ready or self.faiss_index is None:
return []
try:
# بررسی کش
cache_key = f"search:{hashlib.md5(query.encode()).hexdigest()}:{top_k}"
cached = self.cache_system.get(cache_key, 'search')
if cached:
return cached
# تولید embedding برای query
query_embedding = self.embedder.encode([query], convert_to_tensor=True)
query_embedding = query_embedding.cpu().numpy()
faiss.normalize_L2(query_embedding)
# جستجو در ایندکس
similarities, indices = self.faiss_index.search(query_embedding, top_k * 2)
results = []
for i, (similarity, idx) in enumerate(zip(similarities[0], indices[0])):
if similarity < threshold:
continue
if idx < len(self.documents):
doc = self.documents[idx]
results.append({
'rank': i + 1,
'document_id': doc[0],
'url': doc[1],
'title': doc[2],
'content_preview': doc[3][:300] + '...' if len(doc[3]) > 300 else doc[3],
'similarity_score': float(similarity),
'relevance_category': self._categorize_relevance(similarity)
})
# مرتب‌سازی نهایی
results = results[:top_k]
# ذخیره در کش
self.cache_system.set(cache_key, results, 'search', ttl_seconds=1800)
return results
except Exception as e:
logger.error(f"خطا در جستجو: {e}")
return []
def _categorize_relevance(self, similarity: float) -> str:
"""دسته‌بندی میزان ارتباط"""
if similarity >= 0.8:
return "بسیار مرتبط"
elif similarity >= 0.6:
return "مرتبط"
elif similarity >= 0.4:
return "نسبتاً مرتبط"
else:
return "کم‌ارتباط"
def _save_index(self):
"""ذخیره ایندکس و embeddings"""
try:
if self.faiss_index:
faiss.write_index(self.faiss_index, VECTOR_INDEX_PATH)
if self.document_embeddings is not None:
with open(EMBEDDINGS_CACHE_PATH, 'wb') as f:
pickle.dump({
'embeddings': self.document_embeddings,
'documents_info': [(doc[0], doc[1], doc[2]) for doc in self.documents]
}, f)
logger.info("ایندکس ذخیره شد")
except Exception as e:
logger.error(f"خطا در ذخیره ایندکس: {e}")
def _load_existing_index(self):
"""بارگذاری ایندکس موجود"""
try:
self.faiss_index = faiss.read_index(VECTOR_INDEX_PATH)
with open(EMBEDDINGS_CACHE_PATH, 'rb') as f:
cache_data = pickle.load(f)
self.document_embeddings = cache_data['embeddings']
# بازسازی documents از اطلاعات ذخیره شده
# نیاز به query از دیتابیس برای محتوای کامل
logger.info("ایندکس موجود بارگذاری شد")
except Exception as e:
logger.error(f"خطا در بارگذاری ایندکس: {e}")
# === سیستم اسکرپر ساده ===
class SimpleWebScraper:
"""سیستم اسکرپر ساده برای تست"""
def __init__(self):
self.session = requests.Session()
self.session.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
})
def scrape_document(self, url: str) -> Dict:
"""اسکرپ ساده یک سند"""
try:
response = self.session.get(url, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.content, 'html.parser')
# استخراج عنوان
title = ""
title_tag = soup.find('title')
if title_tag:
title = title_tag.get_text().strip()
# استخراج محتوا
content = ""
for tag in soup.find_all(['p', 'div', 'article']):
if tag.get_text().strip():
content += tag.get_text().strip() + "\n"
return {
'status': 'موفق',
'title': title,
'content': content,
'source_info': {'name': urlparse(url).netloc},
'quality_assessment': {'overall_score': 0.7}
}
except Exception as e:
logger.error(f"خطا در اسکرپ {url}: {e}")
return {
'status': 'ناموفق',
'error': str(e)
}
# === اپلیکیشن اصلی پیشرفته ===
class AdvancedLegalScrapingApp:
"""اپلیکیشن اصلی پیشرفته اسکرپینگ حقوقی"""
def __init__(self):
logger.info("🚀 شروع راه‌اندازی سیستم پیشرفته...")
# اجزای اصلی سیستم
self.cache_system = IntelligentCacheSystem()
self.scoring_system = AdvancedScoringSystem()
self.db_manager = AdvancedDatabaseManager()
self.classification_system = IntelligentClassificationSystem(self.cache_system)
self.search_engine = SemanticSearchEngine(self.cache_system)
self.scraper = SimpleWebScraper()
# متریک‌های سیستم
self.system_metrics = SystemMetrics()
logger.info("✅ سیستم آماده است")
def initialize_models(self, progress_callback=None) -> str:
"""مقداردهی مدل‌ها"""
try:
if progress_callback:
progress_callback("بارگذاری مدل‌های طبقه‌بندی...", 0.3)
self.classification_system.load_models()
if progress_callback:
progress_callback("مقداردهی موتور جستجو...", 0.7)
self.search_engine.initialize()
if progress_callback:
progress_callback("تکمیل مقداردهی", 1.0)
return "✅ تمام مدل‌ها با موفقیت بارگذاری شدند"
except Exception as e:
error_msg = f"❌ خطا در بارگذاری مدل‌ها: {str(e)}"
logger.error(error_msg)
return error_msg
def process_urls_intelligent(self, urls_text: str) -> Tuple[str, str]:
"""پردازش هوشمند URLs"""
if not urls_text or not urls_text.strip():
return "❌ لطفاً URLs را وارد کنید", ""
urls = [url.strip() for url in urls_text.split('\n') if url.strip()]
if not urls:
return "❌ URL معتبر یافت نشد", ""
results = []
total_processing_time = 0
for url in urls[:5]: # محدودیت برای تست
start_time = time.time()
try:
# اسکرپ سند
scraped_result = self.scraper.scrape_document(url)
if scraped_result.get('status') == 'موفق':
# طبقه‌بندی
classification = self.classification_system.classify_document(
scraped_result.get('content', '')
)
# امتیازدهی
quality_scores = self.scoring_system.calculate_comprehensive_score(
scraped_result.get('content', ''),
scraped_result.get('source_info', {}),
classification.get('legal_entities', [])
)
# ساخت ProcessingResult
processing_result = ProcessingResult(
url=url,
title=scraped_result.get('title', ''),
content=scraped_result.get('content', ''),
source=scraped_result.get('source_info', {}).get('name', ''),
quality_score=quality_scores.get('final_score', 0),
classification=classification.get('rule_based_classification', {}),
sentiment_score=0.5,
legal_entities=classification.get('legal_entities', []),
embeddings=classification.get('embedding'),
processing_time=classification.get('processing_time', 0)
)
# ذخیره در دیتابیس
self.db_manager.save_document_advanced(processing_result)
# ذخیره در کش
cache_data = {
'title': processing_result.title,
'quality_score': processing_result.quality_score,
'classification': processing_result.classification,
'legal_entities': processing_result.legal_entities,
'status': 'موفق'
}
self.cache_system.set(url, cache_data, 'comprehensive', ttl_seconds=7200)
results.append(cache_data)
else:
results.append({'status': 'ناموفق', 'error': scraped_result.get('error', '')})
total_processing_time += time.time() - start_time
except Exception as e:
logger.error(f"خطا در پردازش {url}: {e}")
results.append({'status': 'ناموفق', 'error': str(e)})
# تأخیر بین درخواست‌ها
time.sleep(1)
# تولید گزارش جامع
successful = sum(1 for r in results if r.get('status') == 'موفق')
failed = len(results) - successful
avg_quality = sum(r.get('quality_score', 0) for r in results if r.get('quality_score')) / max(successful, 1)
summary = f"""
📊 **گزارش پردازش هوشمند**
✅ **موفق**: {successful} سند
❌ **ناموفق**: {failed} سند
📈 **میانگین کیفیت**: {avg_quality:.1f}/100
⏱️ **زمان کل**: {total_processing_time:.2f} ثانیه
🎯 **نتایج کیفیت**:
• بالا (>80): {sum(1 for r in results if r.get('quality_score', 0) > 80)}
• متوسط (50-80): {sum(1 for r in results if 50 <= r.get('quality_score', 0) <= 80)}
• پایین (<50): {sum(1 for r in results if 0 < r.get('quality_score', 0) < 50)}
"""
# جزئیات
details = "\n".join(
f"📄 {r.get('title', 'بدون عنوان')[:50]}... - امتیاز: {r.get('quality_score', 0):.1f}"
for r in results if r.get('status') == 'موفق'
)
return summary, details
def build_intelligent_search_index(self, progress_callback=None) -> str:
"""ساخت ایندکس جستجوی هوشمند"""
try:
if progress_callback:
progress_callback("دریافت اسناد از دیتابیس...", 0.1)
documents = self.db_manager.get_documents_with_embeddings()
if not documents:
return "❌ هیچ سندی برای ایندکس‌سازی یافت نشد"
success = self.search_engine.build_index(documents, progress_callback)
if success:
return f"✅ ایندکس جستجو با {len(documents)} سند آماده شد"
else:
return "❌ خطا در ساخت ایندکس"
except Exception as e:
return f"❌ خطا در ساخت ایندکس: {str(e)}"
def intelligent_semantic_search(self, query: str, limit: int = 10) -> Tuple[str, pd.DataFrame]:
"""جستجوی معنایی هوشمند"""
if not query or not query.strip():
return "❌ لطفاً عبارت جستجو را وارد کنید", pd.DataFrame()
try:
results = self.search_engine.search(query, top_k=limit)
if not results:
return "❌ نتیجه‌ای یافت نشد", pd.DataFrame()
# تولید خلاصه
summary = f"""
🔍 **نتایج جستجو برای**: "{query}"
📊 **آمار**:
• تعداد نتایج: {len(results)}
• بهترین امتیاز: {max(r['similarity_score'] for r in results):.3f}
• میانگین امتیاز: {sum(r['similarity_score'] for r in results) / len(results):.3f}
🎯 **توزیع ارتباط**:
• بسیار مرتبط: {sum(1 for r in results if r['relevance_category'] == 'بسیار مرتبط')}
• مرتبط: {sum(1 for r in results if r['relevance_category'] == 'مرتبط')}
• نسبتاً مرتبط: {sum(1 for r in results if r['relevance_category'] == 'نسبتاً مرتبط')}
"""
# تولید DataFrame
df_data = []
for result in results:
df_data.append({
'رتبه': result['rank'],
'عنوان': result['title'][:50] + '...' if len(result['title']) > 50 else result['title'],
'امتیاز شباهت': f"{result['similarity_score']:.3f}",
'میزان ارتباط': result['relevance_category'],
'پیش‌نمایش محتوا': result['content_preview'][:100] + '...'
})
df = pd.DataFrame(df_data)
return summary, df
except Exception as e:
return f"❌ خطا در جستجو: {str(e)}", pd.DataFrame()
def export_advanced_data(self) -> Tuple[str, Optional[str]]:
"""صدور داده‌های پیشرفته"""
try:
# دریافت آمار
stats = self.db_manager.get_advanced_stats()
cache_stats = self.cache_system.get_stats()
# تولید گزارش جامع
report = {
'system_info': {
'export_time': datetime.now().isoformat(),
'total_documents': stats['total_documents'],
'cache_performance': cache_stats
},
'database_statistics': stats,
'performance_metrics': {
'cache_hit_ratio': cache_stats['total_accesses'] / max(cache_stats['database_entries'], 1),
'avg_processing_time': stats['average_processing_time']
}
}
# ذخیره در فایل موقت
temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False, encoding='utf-8')
json.dump(report, temp_file, ensure_ascii=False, indent=2)
temp_file.close()
status = f"""
📊 **گزارش صدور داده‌ها**
✅ **موفقیت آمیز**
📄 **تعداد اسناد**: {stats['total_documents']}
⚡ **کارایی کش**: {cache_stats['memory_cache_size']} items در حافظه
📈 **میانگین زمان پردازش**: {stats['average_processing_time']} ثانیه
📅 **زمان صدور**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
"""
return status, temp_file.name
except Exception as e:
return f"❌ خطا در صدور داده‌ها: {str(e)}", None
def get_comprehensive_system_status(self) -> str:
"""وضعیت جامع سیستم"""
try:
# آمار پایگاه داده
db_stats = self.db_manager.get_advanced_stats()
# آمار کش
cache_stats = self.cache_system.get_stats()
# آمار مدل‌ها
models_ready = self.classification_system.is_ready
search_ready = self.search_engine.is_ready
status_parts = [
"## 🏠 وضعیت سیستم پیشرفته اسناد حقوقی",
"",
"### 📊 آمار کلی",
f"• **تعداد کل اسناد**: {db_stats['total_documents']}",
f"• **میانگین زمان پردازش**: {db_stats['average_processing_time']} ثانیه",
f"• **آخرین بروزرسانی**: {db_stats['last_updated'][:19]}",
"",
"### ⚡ عملکرد کش",
f"• **حافظه فعال**: {cache_stats['memory_cache_size']} آیتم",
f"• **پایگاه داده کش**: {cache_stats['database_entries']} ورودی",
f"• **تعداد دسترسی‌ها**: {cache_stats['total_accesses']}",
f"• **میانگین استفاده**: {cache_stats['average_accesses']}",
"",
"### 🧠 وضعیت مدل‌ها",
f"• **سیستم طبقه‌بندی**: {'🟢 آماده' if models_ready else '🔴 غیرفعال'}",
f"• **موتور جستجو**: {'🟢 آماده' if search_ready else '🔴 غیرفعال'}",
"",
"### 📈 آمار منابع"
]
# اضافه کردن آمار منابع
for source, count, avg_words in db_stats.get('source_statistics', []):
if source:
status_parts.append(f"• **{source}**: {count} سند (میانگین {avg_words:.0f} کلمه)")
# وضعیت پردازش
status_parts.extend([
"",
"### 🔧 وضعیت پردازش"
])
for status, count in db_stats.get('status_statistics', []):
status_parts.append(f"• **{status}**: {count} سند")
return "\n".join(status_parts)
except Exception as e:
return f"❌ خطا در دریافت وضعیت سیستم: {str(e)}"
def create_advanced_interface(self):
"""ایجاد رابط کاربری پیشرفته"""
# تنظیمات CSS سفارشی برای بهبود ظاهر
custom_css = """
.rtl { direction: rtl; text-align: right; }
.status-success { color: #10b981; font-weight: bold; }
.status-error { color: #ef4444; font-weight: bold; }
.metric-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 20px;
border-radius: 10px;
color: white;
margin: 10px 0;
}
.gradio-container { font-family: 'Vazir', 'Tahoma', sans-serif; }
"""
with gr.Blocks(
theme=gr.themes.Soft(),
css=custom_css,
title="🏛️ سیستم پیشرفته اسناد حقوقی ایران"
) as interface:
# هدر اصلی
gr.HTML("""
<div style="text-align: center; padding: 20px; background: linear-gradient(90deg, #1e40af, #7c3aed); border-radius: 15px; margin-bottom: 20px;">
<h1 style="color: white; margin: 0; font-size: 2.5em;">🏛️ سیستم پیشرفته اسناد حقوقی ایران</h1>
<p style="color: #e0e7ff; margin: 10px 0 0 0; font-size: 1.2em;">
پلتفرم هوشمند تحلیل و دسته‌بندی متون حقوقی با قابلیت‌های پیشرفته
</p>
</div>
""")
# تب‌های اصلی
with gr.Tabs():
# تب خانه و داشبورد
with gr.Tab("🏠 داشبورد اصلی"):
with gr.Row():
with gr.Column(scale=2):
gr.Markdown("### 🚀 مقداردهی سیستم")
initialize_btn = gr.Button(
"⚡ بارگذاری مدل‌های هوش مصنوعی",
variant="primary",
size="lg"
)
initialization_status = gr.Textbox(
label="وضعیت مقداردهی",
interactive=False,
elem_classes=["rtl"]
)
with gr.Column(scale=1):
gr.Markdown("### 📊 آمار سریع")
quick_stats = gr.HTML()
# تب پردازش هوشمند
with gr.Tab("🤖 پردازش هوشمند اسناد"):
gr.Markdown("### 🧠 پردازش و تحلیل هوشمند اسناد حقوقی")
with gr.Row():
urls_input = gr.Textbox(
label="📝 آدرس اسناد (هر خط یک URL)",
placeholder="https://rc.majlis.ir/fa/law/show/123456\nhttps://www.dotic.ir/portal/law/789",
lines=5,
elem_classes=["rtl"]
)
with gr.Row():
process_intelligent_btn = gr.Button(
"🚀 شروع پردازش هوشمند",
variant="primary",
size="lg"
)
clear_cache_btn = gr.Button("🗑️ پاک کردن کش", variant="secondary")
with gr.Row():
intelligent_summary = gr.Textbox(
label="📊 گزارش جامع پردازش",
interactive=False,
lines=8,
elem_classes=["rtl"]
)
intelligent_details = gr.Textbox(
label="📋 جزئیات و امتیازها",
interactive=False,
lines=8,
elem_classes=["rtl"]
)
# تب جستجوی هوشمند
with gr.Tab("🔍 جستجوی معنایی پیشرفته"):
gr.Markdown("### 🎯 جستجوی معنایی با الگوریتم‌های پیشرفته")
with gr.Row():
build_advanced_index_btn = gr.Button("🔧 ساخت ایندکس هوشمند", variant="secondary")
advanced_index_status = gr.Textbox(
label="📈 وضعیت ایندکس",
interactive=False,
elem_classes=["rtl"]
)
with gr.Row():
search_query = gr.Textbox(
label="🔍 عبارت جستجو",
placeholder="مسئولیت کیفری اشخاص حقوقی",
elem_classes=["rtl"],
scale=3
)
search_limit = gr.Slider(
minimum=5, maximum=20, value=10, step=1,
label="📊 تعداد نتایج", scale=1
)
advanced_search_btn = gr.Button("🎯 جستجوی هوشمند", variant="primary")
advanced_search_results_text = gr.Markdown(elem_classes=["rtl"])
advanced_search_results_df = gr.DataFrame(
label="📋 نتایج تفصیلی",
interactive=False
)
# تب مدیریت سیستم
with gr.Tab("📊 مدیریت سیستم پیشرفته"):
with gr.Row():
with gr.Column():
gr.Markdown("### 🏥 وضعیت جامع سیستم")
comprehensive_status = gr.Markdown(elem_classes=["rtl"])
with gr.Row():
refresh_status_btn = gr.Button("🔄 بروزرسانی")
optimize_system_btn = gr.Button("⚡ بهینه‌سازی سیستم")
with gr.Column():
gr.Markdown("### 📤 صدور و پشتیبان‌گیری")
advanced_export_btn = gr.Button("📊 صدور داده‌های پیشرفته", variant="primary")
advanced_export_status = gr.Textbox(
label="📋 وضعیت صدور",
interactive=False,
elem_classes=["rtl"]
)
advanced_export_file = gr.File(label="📁 فایل صادر شده")
# === اتصال Event Handlers ===
# مقداردهی سیستم
initialize_btn.click(
fn=self.initialize_models,
outputs=[initialization_status],
show_progress=True
)
# پردازش هوشمند
process_intelligent_btn.click(
fn=self.process_urls_intelligent,
inputs=[urls_input],
outputs=[intelligent_summary, intelligent_details],
show_progress=True
)
# پاک کردن کش
clear_cache_btn.click(
fn=lambda: self.cache_system.cleanup_expired() or "🗑️ کش پاکسازی شد",
outputs=[intelligent_summary]
)
# ساخت ایندکس هوشمند
build_advanced_index_btn.click(
fn=self.build_intelligent_search_index,
outputs=[advanced_index_status],
show_progress=True
)
# جستجوی هوشمند
advanced_search_btn.click(
fn=self.intelligent_semantic_search,
inputs=[search_query, search_limit],
outputs=[advanced_search_results_text, advanced_search_results_df]
)
# مدیریت سیستم
refresh_status_btn.click(
fn=self.get_comprehensive_system_status,
outputs=[comprehensive_status]
)
optimize_system_btn.click(
fn=lambda: (gc.collect(), self.cache_system.cleanup_expired(), "⚡ سیستم بهینه‌سازی شد")[2],
outputs=[advanced_export_status]
)
# صدور پیشرفته
advanced_export_btn.click(
fn=self.export_advanced_data,
outputs=[advanced_export_status, advanced_export_file]
)
# بارگذاری اولیه
interface.load(
fn=self.get_comprehensive_system_status,
outputs=[comprehensive_status]
)
return interface
# === تابع اصلی ===
def main():
"""تابع اصلی اجرای برنامه"""
try:
# ایجاد اپلیکیشن
app = AdvancedLegalScrapingApp()
# ایجاد رابط کاربری
interface = app.create_advanced_interface()
# اجرای برنامه
interface.launch(
server_name="0.0.0.0",
server_port=7860,
share=True,
show_error=True,
favicon_path=None,
ssl_verify=False
)
except Exception as e:
logger.error(f"خطا در اجرای برنامه: {e}")
print(f"❌ خطا در راه‌اندازی: {e}")
if __name__ == "__main__":
main()