Spaces:
Paused
Paused
""" | |
پلتفرم پیشرفته هوشمند اسناد حقوقی ایران - نسخه ارتقاء یافته | |
مجهز به مدلهای 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" | |
} | |
} | |
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 | |
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() |