leilaghomashchi commited on
Commit
16dab49
·
verified ·
1 Parent(s): 0e8df19

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +241 -112
app.py CHANGED
@@ -1,7 +1,7 @@
1
  #!/usr/bin/env python3
2
  # -*- coding: utf-8 -*-
3
  """
4
- سیستم benchmark ناشناس‌سازی - ورژن نهایی با الگوهای فوق‌العاده دقیق
5
  """
6
 
7
  import pandas as pd
@@ -33,83 +33,142 @@ def convert_to_serializable(obj):
33
  else:
34
  return obj
35
 
36
- # ===== کلاس پردازش entities با الگوهای فوق‌العاده دقیق =====
37
- class PreciseEntityExtractor:
38
  def __init__(self):
39
- # الگوهای فوق‌العاده دقیق با boundaries مشخص
40
  self.patterns = {
41
- 'COMPANY': [
42
- # شرکت + نام مشخص (فقط اسم شرکت، نه جملات)
43
- r'شرکت\s+پتروشیمی\s+[\u0600-\u06FF\u0750-\u077F]+(?=\s|$|،|\.)',
44
- r'شرکت\s+تولیدی\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]+?(?=\s+(?:با|در|کهربوط|صادر)|$|،|\.)',
45
- r'شرکت\s+پردازش\s+[\u0600-\u06FF\u0750-\u077F\s\u200C\-]+?(?=\s+(?:با|در|که|مربوط)|$|،|\.)',
46
- r'بانک\s+[\u0600-\u06FF\u0750-\u077F]+(?=\s+(?:با|در|که|مربوط|ارائهادر)|$|،|\.)',
47
- r'بانک\s+مرکزی\s+جمهوری\s+اسلامی\s+[\u0600-\u06FF\u0750-\u077F]+',
48
- ],
49
-
50
- 'LOCATION': [
51
- # فقط نام مکان، نه عبارات کامل
52
- r'بندر\s+[\u0600-\u06FF\u0750-\u077F]+(?=\s+(?:برگزار|واقع|در)|$|،|\.)',
53
- r'شهر\s+[\u0600-\u06FF\u0750-\u077F]+(?=\s+(?:برگزار|واقع|در)|$|،|\.)',
54
- r'استان\s+[\u0600-\u06FF\u0750-\u077F]+(?=\s+(?:برگزار|واقع|در)|$|،|\.)',
55
- # نام شهرهای مشخص
56
- r'\b(?:تهران|اصفهان|ماهشهر|عسلویه|بندرعباس|اهواز|شیراز|مشهد|تبریز|کرج|قم|رشت|کرمان|یزد|زاهدان|بوشهر|خرمشهر|آبادان|اراک|قزوین|خوزستان)\b',
57
- # اسامی کشورها
58
- r'جمهوری\s+اسلامی\s+ایران',
59
- r'\b(?:ایران|عراق|کویت|عربستان|امارات|قطر|عمان|بحرین|ترکیه)\b',
60
  ],
61
-
 
62
  'PERSON': [
63
- # فقط اسم کامل افراد
64
- r'آقای\s+[\u0600-\u06FF\u0750-\u077F]+\s+[\u0600-\u06FF\u0750-\u077F]+(?=\s+(?:با|مدیر)|$|،|\.)',
65
- r'خانم\s+[\u0600-\u06FF\u0750-\u077F]+\s+[\u0600-\u06FF\u0750-\u077F]+(?=\s+(?:با|همسر)|$|،|\.)',
66
- r'مدیرعامل\s+[\u0600-\u06FF\u0750-\u077F]+\s+[\u0600-\u06FF\u0750-\u077F]+',
67
- r'مدیر\s+مالی\s+[\u0600-\u06FF\u0750-\u077F]+\s+[\u0600-\u06FF\u0750-\u077F]+',
 
 
 
 
68
  ],
69
-
70
- 'DATE': [
71
- # فقط تاریخ‌های مشخص، نه عبارات طولانی
72
- r'[۰-۹\u06F0-\u06F9]{1,2}\s+(?:فروردین|اردیبهشت|خرداد|تیر|مرداد|شهریور|مهر|آبان|آذر|دی|بهمن|اسفند)\s+[۰-۹\u06F0-\u06F9]{4}',
73
- r'\d{4}\/\d{1,2}\/\d{1,2}',
74
- # حذف الگوی طولانی "سال مالی منتهی به..."
 
75
  ],
76
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  'PHONE': [
78
- # شماره‌های تلفن دقیق
79
- r'09\d{9}(?!\d)',
80
- r'021-\d{8}',
81
- r'0\d{2,3}-\d{7,8}',
 
82
  ],
83
-
84
- 'EMAIL': [
85
- r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}',
 
 
86
  ],
87
-
88
- 'AMOUNT': [
89
- # فقط مبالغ خالص
90
- r'\d{6,}\s*تومان',
91
- r'\d+\s*درصد',
 
 
 
 
92
  ],
93
-
94
- 'ACCOUNT': [
95
- r'\d{3}-\d{3}-\d{3}-\d',
96
- r'\d{4}-\d{4}-\d{4}-\d{4}',
 
97
  ],
98
-
99
- 'ID_NUMBER': [
100
- # کدهای ملی و شناسه دقیق
101
- r'کد\s+ملی\s+\d{10}',
102
- r'شناسه\s+ملی\s+\d{11}',
103
- r'(?<!\d)\d{10}(?!\d)',
104
- r'(?<!\d)\d{11}(?!\d)',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  ],
106
-
107
- 'DOCUMENT_NUMBER': [
108
- r'فاکتور\s+شماره\s+[A-Z0-9-]+',
109
- r'چک\s+شماره\s+\d+',
110
- r'گزارش\s+شماره\s+[A-Z0-9-]+',
111
- ]
112
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
 
114
  def clean_entity(self, text):
115
  """تمیز کردن دقیق entity"""
@@ -119,15 +178,43 @@ class PreciseEntityExtractor:
119
  text = re.sub(r'\s+', ' ', text).strip()
120
  return text
121
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  def is_valid_entity(self, text, category):
123
  """بررسی دقیق‌تر معتبر بودن entity"""
124
- if len(text) < 3 or len(text) > 60:
 
 
 
 
125
  return False
126
 
127
  # کلمات ممنوع که نباید entity باشند
128
  forbidden_words = [
129
  'شد', 'کرد', 'است', 'بود', 'در', 'که', 'با', 'از', 'به', 'را', 'و', 'یا',
130
- رکت', 'بانک', 'آقای', 'خانم', 'تومان', 'مبلغ', 'برگزار', 'مطرح', 'واقع'
131
  ]
132
 
133
  if text.lower().strip() in forbidden_words:
@@ -155,16 +242,27 @@ class PreciseEntityExtractor:
155
  if 'سال مالی' in text:
156
  return False
157
 
 
 
 
 
 
158
  return True
159
 
160
  def extract_entities(self, text):
161
- """استخراج entities با دقت بالا"""
162
  if not text or text.strip() == '':
163
  return {}
164
 
165
  entities = {}
 
166
 
167
- for category, pattern_list in self.patterns.items():
 
 
 
 
 
168
  found_entities = []
169
 
170
  for pattern_str in pattern_list:
@@ -173,9 +271,20 @@ class PreciseEntityExtractor:
173
  matches = pattern.finditer(text)
174
 
175
  for match in matches:
176
- entity = self.clean_entity(match.group(0))
177
- if self.is_valid_entity(entity, category):
178
- found_entities.append(entity)
 
 
 
 
 
 
 
 
 
 
 
179
 
180
  except re.error as e:
181
  logger.error(f"Regex error in pattern {pattern_str}: {e}")
@@ -204,6 +313,7 @@ class PreciseEntityExtractor:
204
  return {}
205
 
206
  codes = {}
 
207
  pattern = r'([a-zA-Z_]+)_(\d{3})'
208
 
209
  try:
@@ -225,15 +335,15 @@ class PreciseEntityExtractor:
225
 
226
  return codes
227
 
228
- # ===== کلاس Benchmark =====
229
- class PreciseAnonymizationBenchmark:
230
  def __init__(self):
231
- self.extractor = PreciseEntityExtractor()
232
 
233
  def analyze_single_row(self, original_text, anonymized_text, row_number):
234
  """تحلیل دقیق یک ردیف"""
235
  print(f"\n{'='*80}")
236
- print(f"تحلیل دقیق ردیف {row_number}")
237
  print(f"{'='*80}")
238
 
239
  print(f"\n📝 متن اصلی ({len(original_text)} کاراکتر):")
@@ -243,7 +353,7 @@ class PreciseAnonymizationBenchmark:
243
  print(f"'{anonymized_text[:150]}{'...' if len(anonymized_text) > 150 else ''}'")
244
 
245
  # استخراج entities از متن اصلی
246
- print(f"\n🔍 Entities دقیق استخراج شده از متن اصلی:")
247
  original_entities = self.extractor.extract_entities(original_text)
248
 
249
  total_original_entities = 0
@@ -259,12 +369,12 @@ class PreciseAnonymizationBenchmark:
259
  print(f"\n✅ مجموع entities یافت شده: {total_original_entities}")
260
 
261
  # استخراج کدهای ناشناس‌سازی
262
- print(f"\n🔍 کدهای ناشناس‌سازی:")
263
  anonymized_codes = self.extractor.extract_anonymized_codes(anonymized_text)
264
 
265
  total_anonymized_codes = 0
266
  for category, codes in anonymized_codes.items():
267
- print(f"\n 🔒 {category} ({len(codes)} عدد):")
268
  for i, code in enumerate(codes, 1):
269
  print(f" {i}. '{code}'")
270
  total_anonymized_codes += len(codes)
@@ -275,7 +385,7 @@ class PreciseAnonymizationBenchmark:
275
  print(f"\n✅ مجموع کدهای ناشناس‌سازی: {total_anonymized_codes}")
276
 
277
  # تطبیق دقیق entities و codes
278
- print(f"\n🔄 تطبیق Entities با کدهای ناشناس‌سازی:")
279
  all_categories = set(original_entities.keys()) | set(anonymized_codes.keys())
280
 
281
  for category in sorted(all_categories):
@@ -290,6 +400,14 @@ class PreciseAnonymizationBenchmark:
290
  print(f" لیست اصلی: {original_entities[category]}")
291
  if anon_count > 0:
292
  print(f" لیست کدها: {anonymized_codes[category]}")
 
 
 
 
 
 
 
 
293
 
294
  # محاسبه متریک‌ها
295
  category_metrics = {}
@@ -323,7 +441,7 @@ class PreciseAnonymizationBenchmark:
323
  overall_f1 = 2 * (overall_precision * overall_recall) / (overall_precision + overall_recall) if (overall_precision + overall_recall) > 0 else 0
324
  accuracy = total_tp / total_original_entities if total_original_entities > 0 else 0
325
 
326
- print(f"\n🎯 متریک‌های نهایی ردیف {row_number}:")
327
  print(f" TP: {total_tp}, FP: {total_fp}, FN: {total_fn}")
328
  print(f" Precision: {overall_precision:.4f}")
329
  print(f" Recall: {overall_recall:.4f}")
@@ -375,7 +493,7 @@ class PreciseAnonymizationBenchmark:
375
  all_analysis = []
376
 
377
  for index, row in df.iterrows():
378
- print(f"\n🔄 پردازش ردیف {index + 1} از {len(df)}")
379
 
380
  original_text = str(row['original_text']) if pd.notna(row['original_text']) else ""
381
  anonymized_text = str(row['anonymized_text']) if pd.notna(row['anonymized_text']) else ""
@@ -411,15 +529,15 @@ class PreciseAnonymizationBenchmark:
411
  except Exception as e:
412
  return f"❌ خطا در پردازش: {str(e)}"
413
 
414
- # ===== رابط Gradio =====
415
  def process_uploaded_file(file):
416
  """پردازش فایل آپلود شده"""
417
  if file is None:
418
  return "❌ لطفاً ابتدا فایل CSV را آپلود کنید.", None
419
 
420
- print(f"\n🚀 شروع تحلیل دقیق فایل: {file.name}")
421
 
422
- benchmark = PreciseAnonymizationBenchmark()
423
  result = benchmark.process_csv(file.name)
424
 
425
  if isinstance(result, str):
@@ -445,10 +563,10 @@ def process_uploaded_file(file):
445
  # گزارش نهایی
446
  report = f"""
447
  {'='*80}
448
- 🎯 گزارش نهایی Benchmark دقیق فایل شما
449
  {'='*80}
450
 
451
- 📈 آمار کلی (با الگوهای دقیق):
452
  • تعداد ردیف‌های پردازش شده: {total_rows}
453
  • مجموع Entities دقیق در همه ردیف‌ها: {total_original}
454
  • مجموع کدهای ناشناس‌سازی: {total_anonymized}
@@ -462,19 +580,26 @@ def process_uploaded_file(file):
462
  • F1-Score: {avg_f1:.4f}
463
  • Accuracy: {avg_accuracy:.4f}
464
 
465
- 📊 توضیحات:
466
- • الگوهای regex دقیق‌تر شدهاند
467
- entities اضافی و غلط حذف شده‌اند
468
- فقط entities واقعی و معنادار شناسایی می‌شوند
469
- جزئیات کامل در بالا نمایش داده شده
 
 
 
 
470
 
471
- حالا تعداد entities منطقی‌تر و دقیق‌تر است!
 
 
 
472
  """
473
 
474
  # ذخیره نتایج
475
  try:
476
- results_df.to_csv("precise_benchmark_results.csv", index=False, encoding='utf-8-sig')
477
- print("✅ نتایج دقیق در فایل precise_benchmark_results.csv ذخیره شد")
478
  except Exception as e:
479
  print(f"⚠️ خطا در ذخیره فایل: {e}")
480
 
@@ -488,17 +613,17 @@ def process_uploaded_file(file):
488
 
489
  def download_results():
490
  """دانلود نتایج"""
491
- if os.path.exists("precise_benchmark_results.csv"):
492
- return "precise_benchmark_results.csv"
493
  return None
494
 
495
  # ===== رابط اصلی =====
496
  def main():
497
- with gr.Blocks(title="Ultra Precise Benchmark", theme=gr.themes.Soft()) as demo:
498
 
499
  gr.HTML("""
500
  <h1 style='text-align: center; color: #2E86AB; margin-bottom: 30px;'>
501
- 🎯 سیستم Benchmark فوق‌العاده دقیق ناشناس‌سازی
502
  </h1>
503
  """)
504
 
@@ -506,13 +631,17 @@ def main():
506
  with gr.Column():
507
  gr.HTML("""
508
  <div style='background: #e8f5e8; padding: 15px; border-radius: 10px; margin-bottom: 15px;'>
509
- <h3>🚀 ویژگی‌های ورژن دقیق:</h3>
510
  <ul>
511
- <li><b>الگوهای فوق‌العاده دقیق:</b> حذف entities غلط و اضافی</li>
512
- <li><b>تمیزکاری پیشرفته:</b> حذف کلمات اضافی مانندرگزار شد"</li>
513
- <li><b>اعتبارسنجی هوشمند:</b> بررسی معناداری هر entity</li>
514
- <li><b>حذف تکراری هوشمند:</b> entities که زیرمجموعه دیگران هستند حذف می‌شوند</li>
515
- <li><b>نمایش مقایسه‌ای:</b> entities vs کدهای ناشناس‌سازی</li>
 
 
 
 
516
  </ul>
517
  </div>
518
  """)
@@ -523,14 +652,14 @@ def main():
523
  file_count="single"
524
  )
525
 
526
- process_btn = gr.Button("🎯 تحلیل فوق‌العاده دقیق", variant="primary", size="lg")
527
 
528
  with gr.Row():
529
  with gr.Column():
530
- gr.HTML("<h3>📊 گزارش دقیق + لیست Entities صحیح</h3>")
531
 
532
  results_output = gr.Textbox(
533
- label="گزارش کامل با entities دقیق",
534
  lines=30,
535
  max_lines=35,
536
  interactive=False
@@ -538,18 +667,18 @@ def main():
538
 
539
  with gr.Row():
540
  with gr.Column():
541
- gr.HTML("<h3>📋 جدول نتایج دقیق</h3>")
542
 
543
  results_table = gr.Dataframe(
544
- label="متریک‌های دقیق هر ردیف",
545
  interactive=False,
546
  wrap=True
547
  )
548
 
549
  with gr.Row():
550
  with gr.Column():
551
- download_btn = gr.Button("💾 دانلود نتایج دقیق", variant="secondary")
552
- download_file = gr.File(label="فایل نتایج دقیق", visible=False)
553
 
554
  # Event handlers
555
  process_btn.click(
 
1
  #!/usr/bin/env python3
2
  # -*- coding: utf-8 -*-
3
  """
4
+ سیستم benchmark ناشناس‌سازی - ورژن آپدیت شده با الگوهای جامع و دقیق
5
  """
6
 
7
  import pandas as pd
 
33
  else:
34
  return obj
35
 
36
+ # ===== کلاس پردازش entities با الگوهای آپدیت شده =====
37
+ class UpdatedEntityExtractor:
38
  def __init__(self):
39
+ # الگوهای آپدیت شده براساس سیستم ناشناس‌سازی بهبود یافته
40
  self.patterns = {
41
+ # آدرس‌های کامل - اولویت بالا با پوشش میدان و برج
42
+ 'FULL_ADDRESS': [
43
+ # الگوی آدرس کامل: شهر + میدان + برج + طبقه + واحد
44
+ r'(?:تهران|اصفهان|مشهد|شیراز|کرج|اهواز|قم|رشت|کرمان|یزد|بوشهر|ارومیه|همدان|بندر عباس|ساری|اردبیل|خرم‌آباد|ایلام|بیرجند|گرگان|زنجان|سنندج|شهرکرد|سبزوار|قزوین|زاهدان|خوی|مراغه|کاشان|نجف‌آباد|شاهین‌شهر|ملایر|آبادان|دزفول|بابل|آمل|شاهرود|گنبد کاووس|خرمشهر|جهرم|فسا|مرودشت|لار|داراب|فیروزآباد|کازرون|سپیدان|نی‌ریز|استهبان|فارسان|میانه|ورامین|قرچک|ری|پاکدشت|دماوند|فیروزکوه|شهریار|اسلام‌شهر|ملارد|قدس|بهارستان|چهاردانگه)،\s*(?:میدان|خیابان|کوچه|شهرک|بلوار|کوی|محله)\s+[آ-ی‌ّٰ-ٹ\s]+(?\s*(?:برج|ساختمانجتمع)\s+[آ-ی‌ّٰ-ٹ\s]+)?(?:،\s*(?:طبقه|واحد)\s+[آ-ی‌ّٰ-ٹ\d\s]+)?(?:،\s*واحد\s+[آ-ی‌ّٰ-ٹ\d\s]+)?',
45
+
46
+ # الگوی آدرس کامل: شهر + خیابان + کوچه + پلاک + طبقه
47
+ r'(?:تهران|اصفهان|مشهد|شیراز|کرج|اهواز|قم|رشت|کرمان|یزد|بوشهر|ارومیه|همدان|بندر عباس|ساری|اردبیل|خرم‌آباد|ایلام|بیرجند|گرگان|زنجان|سنندج|شهرکرد|سبزوار|قزوین|زاهدان|خوی|مراغه|کاشان|نجف‌آباد|شاهین‌شهر|ملایر|آبادان|دزفول|بابل|آمل|شاهرود|گنبد کاووس|خرمشهر|جهرم|فسا|مرودشت|لار|داراب|فیروزآباد|کازرون|سپیدان|نی‌ریز|استهبان|فارسان|میانه|ورامین|قرچک|ری|پاکدشت|دماوند|فیروزکوه|شهریار|اسلام‌شهر|ملارد|قدس|بهارستان|چهاردانگه)،\s*(?:خیابان|کوچه|شهرک|بلوار|میدان|کوی|محله)\s+[آ-ی‌ّٰ-ٹ\s]+(?:،\s*(?:خیابان|کوچه|بلوار|کوی)\s+[آ-ی‌ّٰ-ٹ\s]+)?(?:،\s*پلاک\s+\d+)?(?:،\s*(?:طبقه|واحد)\s+[آ-ی‌ّٰ-ٹ\d\s]+)?',
48
+
49
+ # الگوی آدرس با ش��رک
50
+ r'(?:تهران|اصفهان|مشهد|شیراز|کرج|اهواز|قم|رشت|کرمان|یزد|بوشهر|ارومیه|همدان|بندر عباس|ساری|اردبیل|خرم‌آباد|ایلام|بیرجند|گرگان|زنجان|سنندج|شهرکرد|سبزوار|قزوین|زاهدان|خوی|مراغه|کاشان|نجف‌آباد|شاهین‌شهر|ملایر|آبادان|دزفول|بابل|آمل|شاهرود|گنبد کاووس|خرمشهر|جهرم|فسا|مرودشت|لار|داراب|فیروزآباد|کازرون|سپیدان|نی‌ریز|استهبان|فارسان|میانه|ورامین|قرچک|ری|پاکدشت|دماوند|فیروزکوه|شهریار|اسلام‌شهر|ملارد|قدس|بهارستان|چهاردانگه)،\s*شهرک\s+[آ-ی‌ّٰ-ٹ\s]+،\s*(?:خیابان|کوچه|بلوار)\s+[آ-ی‌ّٰ-ٹ\s]+(?:،\s*پلاک\s+\d+)?',
51
+
52
+ # الگوی ساده‌تر برای آدرس‌های کوتاه‌تر
53
+ r'خیابان\s+[آ-ی‌ّٰ-ٹ\s]+،\s*کوچه\s+[آ-ی‌ّٰ-ٹ\s]+،\s*پلاک\s+\d+(?\s*(?:طبقه|واحد)\s+[آ-ی‌ّٰ-ٹ\d\s]+)?',
 
 
 
 
 
 
54
  ],
55
+
56
+ # اسامی اشخاص - الگوهای دقیق‌تر شامل خانم
57
  'PERSON': [
58
+ r'آقای\s+[آ-ی‌ّٰ-ٹ]+\s+[آ-ی‌ّٰ-ٹ]+(?=\s+با\s+کد|\s+مدیر|$|،|\.)',
59
+ r'خانم\s+[آ-ی‌ّٰ-ٹ]+\s+[آ-ی‌ّٰ-ٹ]+(?=\s+با\s+کد|\s+با\s+موبایل|$|،|\.)',
60
+ r'مهندس\s+[آ-ی‌ّٰ-ٹ]+\s+[آ-ی‌ّٰ-ٹ]+(?=\s+با\s+کد|$|،|\.)',
61
+ r'دکتر\s+[آ-ی‌ّٰ-ٹ]+\s+[آ-ی‌ّٰ-ٹ]+(?=\s+با\s+کد|$|،|\.)',
62
+ r'مدیر\s+مالی\s+خانم\s+[آ-ی‌ّٰ-ٹ]+\s+[آ-ی‌ّٰ-ٹ]+',
63
+ r'مدیرعامل\s+[آ-ی‌ّٰ-ٹ]+\s+[آ-ی‌ّٰ-ٹ]+',
64
+ r'Mr\.\s+[A-Z][a-z]+\s+[A-Z][a-z]+(?=\s|,|\.|$)',
65
+ r'Ms\.\s+[A-Z][a-z]+\s+[A-Z][a-z]+(?=\s|,|\.|$)',
66
+ r'Dr\.\s+[A-Z][a-z]+\s+[A-Z][a-z]+(?=\s|,|\.|$)',
67
  ],
68
+
69
+ # کدهای ملی و شناسه‌ها - جداسازی از شماره تلفن
70
+ 'ID_NUMBER': [
71
+ r'کد\s+ملی\s+\d{10}',
72
+ r'شناسه\s+ملی\s+\d{11}',
73
+ r'(?<!09)(?<!021-)(?<![0-9])\d{10}(?![0-9])', # کد ملی 10 رقمی مستقل
74
+ r'(?<!09)(?<!021-)(?<![0-9])\d{11}(?![0-9])', # شناسه 11 رقمی مستقل
75
  ],
76
+
77
+ # مبالغ مالی - جداسازی از شماره تلفن
78
+ 'AMOUNT': [
79
+ r'\d{6,}\s*تومان', # مبالغ 6 رقمی یا بیشتر با کلمه تومان
80
+ r'مبلغ\s+\d{6,}(?:\s*تومان)?',
81
+ r'موجودی\s+حساب\s+[^\s]+\s+حدود\s+\d{6,}\s*تومان',
82
+ r'ارزش\s+روز\s+آن\s+\d{6,}\s*تومان',
83
+ r'میانگین\s+موجودی\s+حساب\s+وی\s+حدود\s+\d{6,}\s*تومان',
84
+ r'\d+\s*درصد\s+مبلغ\s+معادل\s+\d{6,}\s*تومان',
85
+ r'\$\d+(?:,\d{3})*(?:\.\d+)?',
86
+ ],
87
+
88
+ # شماره حساب و کارت بانکی - جداسازی دقیق
89
+ 'ACCOUNT': [
90
+ r'حساب\s+جاری\s+شماره\s+[\d-]+',
91
+ r'شماره\s+[\d-]{8,}(?=\s+در|\s+بانک)', # شماره حساب
92
+ ],
93
+
94
+ 'CARD_NUMBER': [
95
+ r'شماره\s+کارت\s+مربوطه\s+\d{4}-\d{4}-\d{4}-\d{4}',
96
+ r'\d{4}-\d{4}-\d{4}-\d{4}(?=\s+می‌باشد|\s+نیز)',
97
+ ],
98
+
99
+ # شماره تلفن - فقط شماره، نه کل عبارت
100
  'PHONE': [
101
+ r'09\d{9}(?=\s+را)', # فقط شماره موبایل قبل از "را"
102
+ r'(?<![0-9])09\d{9}(?![0-9])(?!\s+را\s+اعلام)', # شماره موبایل مستقل
103
+ r'موبایل\s+09\d{9}', # با کلمه موبایل
104
+ r'021-\d{8}', # تلفن تهران
105
+ r'0\d{2,3}-?\d{7,8}', # تلفن‌های شهری
106
  ],
107
+
108
+ # تاریخ
109
+ 'DATE': [
110
+ r'\d{4}/\d{1,2}/\d{1,2}',
111
+ r'[۰-۹]{1,2}\s+(?:فروردین|اردیبهشت|خرداد|تیر|مرداد|شهریور|مهر|آبان|آذر|دی|بهمن|اسفند)\s+[۰-۹]{4}',
112
  ],
113
+
114
+ # شرکت‌ها - الگوهای گسترده‌تر اصلاح شده
115
+ 'COMPANY': [
116
+ r'شرکت\s+پردازش\sاده‌های\s+[آ-ی‌ّٰ-ٹ\s]+',
117
+ r'شرکت\s+تولیدی\s+[آ-ی‌ّٰ-ٹ\s]+(?=\s|$|،|\.)',
118
+ r'شرکت\s+[آ-ی‌ّٰ-ٹ\s]+(?=\s|$|،|\.)',
119
+ r'(?<!همین\s)(?<!این\s)(?<!آن\s)بانک\s+[آ-ی‌ّٰ-ٹ\s]+(?=\s|$|،|\.)', # بانک فقط اگر قبلش همین/این/آن نباشد
120
+ r'دفتر\s+حسابداری\s+شرکت',
121
+ r'[A-Z][a-zA-Z\s]+(?:Inc|Corp|Corporation|Company|Ltd|Limited|LLC)',
122
  ],
123
+
124
+ # شعب و واحدهای تجاری - دقیق‌تر شده
125
+ 'BRANCH': [
126
+ r'شعبه\s+[آ-ی‌ّٰ-ٹ\s]+\s+بانک\s+[آ-ی‌ّٰ-ٹ\s]+',
127
+ r'شعبه\s+مرکزی\s+بانک\s+[آ-ی‌ّٰ-ٹ\s]+',
128
  ],
129
+
130
+ # مکان‌ها - شهرها (فقط نام شهرها تنها)
131
+ 'LOCATION': [
132
+ r'\b(?:تهران|اصفهان|مشهد|شیراز|کرج|اهواز|قم|رشت|کرمان|یزد|بوشهر|ارومیه|همدان|بندر عباس|ساری|اردبیل|خرم‌آباد|ایلام|بیرجند|گرگان|زنجان|سنندج|شهرکرد|سبزوار|قزوین|زاهدان|خوی|مراغه|کاشان|نجف‌آباد|شاهین‌شهر|ملایر|آبادان|دزفول|بابل|آمل|شاهرود|گنبد کاووس|خرمشهر|جهرم|فسا|مرودشت|لار|داراب|فیروزآباد|کازرون|سپیدان|نی‌ریز|استهبان|فارسان|میانه|ورامین|قرچک|ری|پاکدشت|دماوند|فیروزکوه|شهریار|اسلام‌شهر|ملارد|قدس|بهارستان|چهاردانگه)\b(?!\s*،)(?!\s+میدان)(?!\s+خیابان)', # فقط نام شهر تنها
133
+ r'استان\s+[آ-ی‌ّٰ-ٹ\s]+',
134
+ r'شهر\s+[آ-ی‌ّٰ-ٹ\s]+',
135
+ ],
136
+
137
+ # شماره فاکتور و اسناد - الگوهای جدید
138
+ 'INVOICE_NUMBER': [
139
+ r'فاکتور\s+شماره\s+[A-Z]+-\d{4}-\d{4}',
140
+ r'[A-Z]+-\d{4}-\d{4}(?=\s+را\s+برای)',
141
+ ],
142
+
143
+ # دفاتر اسناد رسمی
144
+ 'DOCUMENT_OFFICE': [
145
+ r'دفتر\s+اسناد\s+رسمی\s+شماره\s+\d+',
146
+ r'شماره\s+\d+\s+(?:تهران|اصفهان|مشهد|شیراز|کرج)',
147
+ ],
148
+
149
+ # ایمیل
150
+ 'EMAIL': [
151
+ r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}',
152
  ],
 
 
 
 
 
 
153
  }
154
+
155
+ # ترتیب اولویت براساس سیستم ناشناس‌سازی
156
+ self.priority_order = [
157
+ 'FULL_ADDRESS', # بالاترین اولویت - آدرس کامل قبل از قطعات
158
+ 'INVOICE_NUMBER', # شماره فاکتور
159
+ 'DOCUMENT_OFFICE', # دفاتر اسناد رسمی
160
+ 'EMAIL',
161
+ 'CARD_NUMBER', # کارت بانکی
162
+ 'ACCOUNT', # حساب بانکی
163
+ 'PHONE', # شماره تلفن - اولویت بالا
164
+ 'ID_NUMBER', # کد ملی
165
+ 'AMOUNT', # مبالغ مالی
166
+ 'BRANCH', # شعبه
167
+ 'COMPANY', # شرکت
168
+ 'LOCATION', # مکان
169
+ 'DATE', # تاریخ
170
+ 'PERSON', # نام اشخاص
171
+ ]
172
 
173
  def clean_entity(self, text):
174
  """تمیز کردن دقیق entity"""
 
178
  text = re.sub(r'\s+', ' ', text).strip()
179
  return text
180
 
181
+ def is_generic_word(self, text):
182
+ """بررسی کلمات عمومی که نباید entity محسوب شوند"""
183
+ text_clean = text.strip().lower()
184
+
185
+ generic_words = {
186
+ 'همین بانک', 'این بانک', 'آن بانک', 'بانک مذکور',
187
+ 'همین شرکت', 'این شرکت', 'آن شرکت', 'شرکت مذکور',
188
+ 'همین شعبه', 'این شعبه', 'آن شعبه', 'شعبه مذکور',
189
+ 'همین شهر', 'این شهر', 'آن شهر',
190
+ 'شرکت متقاضی', 'دفتر حسابداری شرکت',
191
+ 'متقاضی', 'ایشان', 'وی', 'مشتری',
192
+ 'بانک', 'شرکت', 'شعبه' # کلمات تنها
193
+ }
194
+
195
+ # بررسی exact match
196
+ if text_clean in generic_words:
197
+ return True
198
+
199
+ # بررسی کلمات که با همین/این/آن شروع می‌شوند
200
+ if text_clean.startswith(('همین ', 'این ', 'آن ')):
201
+ return True
202
+
203
+ return len(text.strip()) < 3
204
+
205
  def is_valid_entity(self, text, category):
206
  """بررسی دقیق‌تر معتبر بودن entity"""
207
+ if len(text) < 3 or len(text) > 100:
208
+ return False
209
+
210
+ # بررسی کلمات عمومی
211
+ if self.is_generic_word(text):
212
  return False
213
 
214
  # کلمات ممنوع که نباید entity باشند
215
  forbidden_words = [
216
  'شد', 'کرد', 'است', 'بود', 'در', 'که', 'با', 'از', 'به', 'را', 'و', 'یا',
217
+ ده', 'نموده', 'صادر', 'ارائه', 'معرفی', 'برگزار', 'مطرح', 'واقع'
218
  ]
219
 
220
  if text.lower().strip() in forbidden_words:
 
242
  if 'سال مالی' in text:
243
  return False
244
 
245
+ elif category == 'PERSON':
246
+ # نباید فقط عنوان باشد
247
+ if text.strip() in ['آقای', 'خانم', 'مهندس', 'دکتر']:
248
+ return False
249
+
250
  return True
251
 
252
  def extract_entities(self, text):
253
+ """استخراج entities با دقت بالا و اولویت‌بندی"""
254
  if not text or text.strip() == '':
255
  return {}
256
 
257
  entities = {}
258
+ processed_positions = set()
259
 
260
+ # پردازش براساس اولویت
261
+ for category in self.priority_order:
262
+ if category not in self.patterns:
263
+ continue
264
+
265
+ pattern_list = self.patterns[category]
266
  found_entities = []
267
 
268
  for pattern_str in pattern_list:
 
271
  matches = pattern.finditer(text)
272
 
273
  for match in matches:
274
+ # بررسی تداخل با entities قبلی
275
+ match_start, match_end = match.span()
276
+ overlaps = False
277
+
278
+ for proc_start, proc_end in processed_positions:
279
+ if not (match_end <= proc_start or match_start >= proc_end):
280
+ overlaps = True
281
+ break
282
+
283
+ if not overlaps:
284
+ entity = self.clean_entity(match.group(0))
285
+ if self.is_valid_entity(entity, category):
286
+ found_entities.append(entity)
287
+ processed_positions.add((match_start, match_end))
288
 
289
  except re.error as e:
290
  logger.error(f"Regex error in pattern {pattern_str}: {e}")
 
313
  return {}
314
 
315
  codes = {}
316
+ # الگو براساس سیستم ناشناس‌سازی: category_number
317
  pattern = r'([a-zA-Z_]+)_(\d{3})'
318
 
319
  try:
 
335
 
336
  return codes
337
 
338
+ # ===== کلاس Benchmark آپدیت شده =====
339
+ class UpdatedAnonymizationBenchmark:
340
  def __init__(self):
341
+ self.extractor = UpdatedEntityExtractor()
342
 
343
  def analyze_single_row(self, original_text, anonymized_text, row_number):
344
  """تحلیل دقیق یک ردیف"""
345
  print(f"\n{'='*80}")
346
+ print(f"تحلیل دقیق ردیف {row_number} (ورژن آپدیت شده)")
347
  print(f"{'='*80}")
348
 
349
  print(f"\n📝 متن اصلی ({len(original_text)} کاراکتر):")
 
353
  print(f"'{anonymized_text[:150]}{'...' if len(anonymized_text) > 150 else ''}'")
354
 
355
  # استخراج entities از متن اصلی
356
+ print(f"\n🔍 Entities دقیق استخراج شده از متن اصلی (الگوهای آپدیت شده):")
357
  original_entities = self.extractor.extract_entities(original_text)
358
 
359
  total_original_entities = 0
 
369
  print(f"\n✅ مجموع entities یافت شده: {total_original_entities}")
370
 
371
  # استخراج کدهای ناشناس‌سازی
372
+ print(f"\n🔒 کدهای ناشناس‌سازی:")
373
  anonymized_codes = self.extractor.extract_anonymized_codes(anonymized_text)
374
 
375
  total_anonymized_codes = 0
376
  for category, codes in anonymized_codes.items():
377
+ print(f"\n 🔑 {category} ({len(codes)} عدد):")
378
  for i, code in enumerate(codes, 1):
379
  print(f" {i}. '{code}'")
380
  total_anonymized_codes += len(codes)
 
385
  print(f"\n✅ مجموع کدهای ناشناس‌سازی: {total_anonymized_codes}")
386
 
387
  # تطبیق دقیق entities و codes
388
+ print(f"\n📄 تطبیق Entities با کدهای ناشناس‌سازی:")
389
  all_categories = set(original_entities.keys()) | set(anonymized_codes.keys())
390
 
391
  for category in sorted(all_categories):
 
400
  print(f" لیست اصلی: {original_entities[category]}")
401
  if anon_count > 0:
402
  print(f" لیست کدها: {anonymized_codes[category]}")
403
+
404
+ # وضعیت تطبیق
405
+ if orig_count == anon_count:
406
+ print(f" وضعیت: ✅ تطبیق کامل")
407
+ elif orig_count > anon_count:
408
+ print(f" وضعیت: ⚠️ {orig_count - anon_count} entity از دست رفته")
409
+ else:
410
+ print(f" وضعیت: ⚠️ {anon_count - orig_count} کد اضافی")
411
 
412
  # محاسبه متریک‌ها
413
  category_metrics = {}
 
441
  overall_f1 = 2 * (overall_precision * overall_recall) / (overall_precision + overall_recall) if (overall_precision + overall_recall) > 0 else 0
442
  accuracy = total_tp / total_original_entities if total_original_entities > 0 else 0
443
 
444
+ print(f"\n🎯 متریک‌های نهایی ردیف {row_number} (الگوهای آپدیت شده):")
445
  print(f" TP: {total_tp}, FP: {total_fp}, FN: {total_fn}")
446
  print(f" Precision: {overall_precision:.4f}")
447
  print(f" Recall: {overall_recall:.4f}")
 
493
  all_analysis = []
494
 
495
  for index, row in df.iterrows():
496
+ print(f"\n📄 پردازش ردیف {index + 1} از {len(df)}")
497
 
498
  original_text = str(row['original_text']) if pd.notna(row['original_text']) else ""
499
  anonymized_text = str(row['anonymized_text']) if pd.notna(row['anonymized_text']) else ""
 
529
  except Exception as e:
530
  return f"❌ خطا در پردازش: {str(e)}"
531
 
532
+ # ===== رابط Gradio آپدیت شده =====
533
  def process_uploaded_file(file):
534
  """پردازش فایل آپلود شده"""
535
  if file is None:
536
  return "❌ لطفاً ابتدا فایل CSV را آپلود کنید.", None
537
 
538
+ print(f"\n🚀 شروع تحلیل دقیق فایل (ورژن آپدیت شده): {file.name}")
539
 
540
+ benchmark = UpdatedAnonymizationBenchmark()
541
  result = benchmark.process_csv(file.name)
542
 
543
  if isinstance(result, str):
 
563
  # گزارش نهایی
564
  report = f"""
565
  {'='*80}
566
+ 🎯 گزارش نهایی Benchmark آپدیت شده - براساس سیستم ناشناس‌سازی بهبود یافته
567
  {'='*80}
568
 
569
+ 📈 آمار کلی (الگوهای آپدیت شده براساس سیستم جامع):
570
  • تعداد ردیف‌های پردازش شده: {total_rows}
571
  • مجموع Entities دقیق در همه ردیف‌ها: {total_original}
572
  • مجموع کدهای ناشناس‌سازی: {total_anonymized}
 
580
  • F1-Score: {avg_f1:.4f}
581
  • Accuracy: {avg_accuracy:.4f}
582
 
583
+ 🆕 بهبودهای آپدیت شده:
584
+ آدرس کامل شامل میدان، برج، طبقه و واحد
585
+ نام اشخاص با عنوان خانم و مدیر مالی
586
+ شرکت‌های پیچیده (پردازش داده‌های ایرانیان)
587
+ شماره فاکتور و اسناد رسمی
588
+ • تلفن ثابت شهری (021-)
589
+ • فیلتر کلمات عمومی ("همین بانک", "شرکت متقاضی")
590
+ • اولویت‌بندی بهتر پردازش entities
591
+ • جداسازی دقیق کد ملی از شماره تلفن
592
 
593
+ ✅ الگوهای regex دقیق‌تر و جامع‌تر شده‌اند!
594
+ ✅ entities اضافی و غلط حذف شده‌اند
595
+ ✅ فقط entities واقعی و معنادار شناسایی می‌شوند
596
+ ✅ تطبیق بهتر با سیستم ناشناس‌سازی پیشرفته
597
  """
598
 
599
  # ذخیره نتایج
600
  try:
601
+ results_df.to_csv("updated_benchmark_results.csv", index=False, encoding='utf-8-sig')
602
+ print("✅ نتایج آپدیت شده در فایل updated_benchmark_results.csv ذخیره شد")
603
  except Exception as e:
604
  print(f"⚠️ خطا در ذخیره فایل: {e}")
605
 
 
613
 
614
  def download_results():
615
  """دانلود نتایج"""
616
+ if os.path.exists("updated_benchmark_results.csv"):
617
+ return "updated_benchmark_results.csv"
618
  return None
619
 
620
  # ===== رابط اصلی =====
621
  def main():
622
+ with gr.Blocks(title="آپدیت شده: Ultra Precise Benchmark", theme=gr.themes.Soft()) as demo:
623
 
624
  gr.HTML("""
625
  <h1 style='text-align: center; color: #2E86AB; margin-bottom: 30px;'>
626
+ 🔄 سیستم Benchmark آپدیت شده - براساس الگوهای ناشناس‌سازی پیشرفته
627
  </h1>
628
  """)
629
 
 
631
  with gr.Column():
632
  gr.HTML("""
633
  <div style='background: #e8f5e8; padding: 15px; border-radius: 10px; margin-bottom: 15px;'>
634
+ <h3>🆕 ویژگی‌های آپدیت شده:</h3>
635
  <ul>
636
+ <li><b>آدرس کامل:</b> تهران، میدان ونک، برج میلاد، طبقه 15، واحد 1503</li>
637
+ <li><b>اسامی کامل:</b> خانم زهرا احمدی، مدیر مالی خانم احمدی</li>
638
+ <li><b>شرکت‌های پیچیده:</b> شرکت پردازش داده‌های ایرانیان</li>
639
+ <li><b>شما��ه فاکتور:</b> INV-2024-0156</li>
640
+ <li><b>دفاتر اسناد:</b> دفتر اسناد رسمی شماره 1247 تهران</li>
641
+ <li><b>تلفن ثابت:</b> 021-88776655</li>
642
+ <li><b>فیلتر هوشمند:</b> حذف "همین بانک"، "شرکت متقاضی"</li>
643
+ <li><b>اولویت‌بندی:</b> آدرس کامل قبل از قطعات</li>
644
+ <li><b>جداسازی دقیق:</b> کد ملی از شماره تلفن</li>
645
  </ul>
646
  </div>
647
  """)
 
652
  file_count="single"
653
  )
654
 
655
+ process_btn = gr.Button("🔄 تحلیل با الگوهای آپدیت شده", variant="primary", size="lg")
656
 
657
  with gr.Row():
658
  with gr.Column():
659
+ gr.HTML("<h3>📊 گزارش آپدیت شده + لیست Entities صحیح</h3>")
660
 
661
  results_output = gr.Textbox(
662
+ label="گزارش کامل با entities آپدیت شده",
663
  lines=30,
664
  max_lines=35,
665
  interactive=False
 
667
 
668
  with gr.Row():
669
  with gr.Column():
670
+ gr.HTML("<h3>📋 جدول نتایج آپدیت شده</h3>")
671
 
672
  results_table = gr.Dataframe(
673
+ label="متریک‌های دقیق هر ردیف (ورژن جدید)",
674
  interactive=False,
675
  wrap=True
676
  )
677
 
678
  with gr.Row():
679
  with gr.Column():
680
+ download_btn = gr.Button("💾 دانلود نتایج آپدیت شده", variant="secondary")
681
+ download_file = gr.File(label="فایل نتایج آپدیت شده", visible=False)
682
 
683
  # Event handlers
684
  process_btn.click(