leilaghomashchi commited on
Commit
bb578da
·
verified ·
1 Parent(s): 747bcec

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +119 -102
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
@@ -20,7 +20,6 @@ logger = logging.getLogger(__name__)
20
 
21
  # ===== تابع کمکی برای تبدیل numpy/pandas types =====
22
  def convert_to_serializable(obj):
23
- """تبدیل numpy/pandas types به Python native types برای JSON serialization"""
24
  if isinstance(obj, (np.integer, np.int64, np.int32)):
25
  return int(obj)
26
  elif isinstance(obj, (np.floating, np.float64, np.float32)):
@@ -34,53 +33,42 @@ def convert_to_serializable(obj):
34
  else:
35
  return obj
36
 
37
- # ===== کلاس پردازش entities =====
38
  class EntityExtractor:
39
  def __init__(self):
 
40
  self.patterns = {
41
  'COMPANY': [
42
- r'شرکت\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]+',
43
- r'بانک\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]+',
44
- r'[\u0600-\u06FF\u0750-\u077F\s\u200C]*(?:پتروشیمی|بانک|شرکت|صنایع|تولید)[\u0600-\u06FF\u0750-\u077F\s\u200C]*',
45
- r'[A-Z][a-zA-Z\s]+(?:Inc|Corp|Corporation|Company|Ltd|Limited|LLC)',
 
46
  ],
47
 
48
  'LOCATION': [
49
- r'بندر\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]+',
50
- r'شهر\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]+',
51
- r'استان\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]+',
52
  r'\b(?:تهران|اصفهان|ماهشهر|عسلویه|بندرعباس|اهواز|شیراز|مشهد|تبریز|کرج|قم|رشت|کرمان|یزد|زاهدان|بوشهر|خرمشهر|آبادان|اراک|قزوین|خوزستان)\b',
53
- r'\b(?:ایران|عراق|کویت|عربستان|امارات|قطر|عمان|بحرین|ترکیه|پاکستان|افغانستان)\b',
54
- r'\b(?:London|Paris|Tokyo|New\s+York|Dubai|Singapore|Hong\s+Kong)\b'
55
  ],
56
 
57
  'PERSON': [
58
- r'آقای\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]+',
59
- r'خانم\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]+',
60
- r'مهندس\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]+',
61
- r'دکتر\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]+',
62
- r'مدیرعامل\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]+',
63
- r'[\u0600-\u06FF\u0750-\u077F\s\u200C]+\s+مدیرعامل',
64
- r'Mr\.\s+[a-zA-Z\s]+',
65
- r'Ms\.\s+[a-zA-Z\s]+',
66
- r'Dr\.\s+[a-zA-Z\s]+'
67
  ],
68
 
69
  'DATE': [
70
- r'سال\s+مالی\s+منتهی\s+به\s+[\u06F0-\u06F90-9]{1,2}\s+[\u0600-\u06FF\u0750-\u077F]+\s+[\u06F0-\u06F90-9]{4}',
71
- r'[\u06F0-\u06F90-9]{1,2}\s+(?:فروردین|اردیبهشت|خرداد|تیر|مرداد|شهریور|مهر|آبان|آذر|دی|بهمن|اسفند)\s+[\u06F0-\u06F90-9]{4}',
72
- r'[\u06F0-\u06F90-9]{1,2}\s+[\u0600-\u06FF\u0750-\u077F]+\s+[\u06F0-\u06F90-9]{4}',
73
- r'[\u06F0-\u06F90-9]{4}[/-][\u06F0-\u06F90-9]{1,2}[/-][\u06F0-\u06F90-9]{1,2}',
74
- r'[\u06F0-\u06F90-9]{1,2}[/-][\u06F0-\u06F90-9]{1,2}[/-][\u06F0-\u06F90-9]{4}',
75
- r'(?:[0-9]{1,2})\s*(?:January|February|March|April|May|June|July|August|September|October|November|December)\s*(?:[0-9]{4})',
76
- r'(?:13[0-9]{2}|14[0-9]{2}|20[0-9]{2}|19[0-9]{2})(?=\s|$|،|\.)'
77
  ],
78
 
79
  'PHONE': [
80
- r'(?:شماره[\s]*تماس[\s:]*)?(?:0)?9[\u06F0-\u06F90-9]{9}',
81
- r'(?:تلفن[\s:]*)?(?:0)?[\u06F0-\u06F90-9]{2,3}[-\s]?[\u06F0-\u06F90-9]{7,8}',
82
- r'[\u06F0-\u06F90-9]{11}(?!\d)',
83
- r'\+[0-9]{1,3}[-\s][0-9]{3}[-\s][0-9]{3}[-\s][0-9]{4}',
84
  ],
85
 
86
  'EMAIL': [
@@ -88,23 +76,34 @@ class EntityExtractor:
88
  ],
89
 
90
  'AMOUNT': [
91
- r'\d+(?:,\d{3})*\s*(?:میلیون|میلیارد|هزار)\s*تومان',
92
- r'مبلغ\s+\d+(?:,\d{3})*\s*(?:میلیون|میلیارد|هزار)?\s*تومان',
93
- r'\d+\s*تومان',
94
- r'\$\d+(?:,\d{3})*(?:\.\d+)?(?:\s*(?:million|billion|thousand|M|B|K))?',
95
- r'\d+(?:,\d{3})*\s*ریال'
96
  ],
97
 
98
  'ACCOUNT': [
99
- r'(?:شماره[\s]*حساب[\s:]*)?[\u06F0-\u06F90-9]{3}[-\s]?[\u06F0-\u06F90-9]{3}[-\s]?[\u06F0-\u06F90-9]{6,12}',
100
- r'(?:حساب[\s]*شماره[\s:]*)?[\u06F0-\u06F90-9]{8,20}',
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  ]
102
  }
103
 
104
  def clean_entity(self, text):
105
  """تمیز کردن entity استخراج شده"""
106
  # حذف کلمات اضافی در انتها
107
- text = re.sub(r'\s*(در|که|با|به|از|را|و|یا)\s*$', '', text).strip()
108
  # حذف فاصله‌های اضافی
109
  text = re.sub(r'\s+', ' ', text).strip()
110
  return text
@@ -114,9 +113,15 @@ class EntityExtractor:
114
  if len(text) < 2:
115
  return False
116
 
117
- # کلمات ممنوع
118
- forbidden = ['شد', 'کرد', 'است', 'بود', 'در', 'که', 'با', 'از', 'به', 'را', 'و', 'یا']
119
- if text.lower() in forbidden:
 
 
 
 
 
 
120
  return False
121
 
122
  return True
@@ -183,49 +188,48 @@ class AnonymizationBenchmark:
183
  def __init__(self):
184
  self.extractor = EntityExtractor()
185
 
186
- def analyze_single_row(self, original_text, anonymized_text):
187
- """تحلیل یک ردیف از CSV"""
188
- print(f"\n{'='*60}")
189
- print("تحلیل دقیق ردیف:")
190
- print(f"{'='*60}")
191
 
192
- print(f"\n📝 متن اصلی:")
193
- print(f"'{original_text}'")
194
- print(f"طول: {len(original_text)} کاراکتر")
195
 
196
- print(f"\n🔒 متن ناشناس‌سازی شده:")
197
- print(f"'{anonymized_text}'")
198
- print(f"طول: {len(anonymized_text)} کاراکتر")
199
 
200
  # استخراج entities از متن اصلی
201
- print(f"\n🔍 استخراج Entities از متن اصلی:")
202
  original_entities = self.extractor.extract_entities(original_text)
203
 
204
  total_original_entities = 0
205
  for category, entities in original_entities.items():
206
- print(f" {category}: {len(entities)} عدد")
207
  for i, entity in enumerate(entities, 1):
208
  print(f" {i}. '{entity}'")
209
  total_original_entities += len(entities)
210
 
211
  if not original_entities:
212
- print(" هیچ entity ای یافت نشد!")
213
  else:
214
  print(f"\n✅ مجموع entities یافت شده: {total_original_entities}")
215
 
216
  # استخراج کدهای ناشناس‌سازی
217
- print(f"\n🔍 استخراج کدهای ناشناس‌سازی:")
218
  anonymized_codes = self.extractor.extract_anonymized_codes(anonymized_text)
219
 
220
  total_anonymized_codes = 0
221
  for category, codes in anonymized_codes.items():
222
- print(f" {category}: {len(codes)} عدد")
223
  for i, code in enumerate(codes, 1):
224
  print(f" {i}. '{code}'")
225
  total_anonymized_codes += len(codes)
226
 
227
  if not anonymized_codes:
228
- print(" هیچ کد ناشناس‌سازی یافت نشد!")
229
  else:
230
  print(f"\n✅ مجموع کدهای ناشناس‌سازی: {total_anonymized_codes}")
231
 
@@ -236,6 +240,20 @@ class AnonymizationBenchmark:
236
 
237
  all_categories = set(original_entities.keys()) | set(anonymized_codes.keys())
238
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
  for category in all_categories:
240
  original_count = len(original_entities.get(category, []))
241
  anonymized_count = len(anonymized_codes.get(category, []))
@@ -248,14 +266,13 @@ class AnonymizationBenchmark:
248
  recall = tp / (tp + fn) if (tp + fn) > 0 else 0
249
  f1_score = 2 * (precision * recall) / (precision + recall) if (precision + recall) > 0 else 0
250
 
251
- print(f"\n {category}:")
252
- print(f" Original: {original_count}, Anonymized: {anonymized_count}")
253
  print(f" TP: {tp}, FP: {fp}, FN: {fn}")
254
  print(f" Precision: {precision:.4f}, Recall: {recall:.4f}, F1: {f1_score:.4f}")
255
 
256
  category_metrics[category] = {
257
- 'original_count': original_count,
258
- 'anonymized_count': anonymized_count,
259
  'tp': tp, 'fp': fp, 'fn': fn,
260
  'precision': precision, 'recall': recall, 'f1_score': f1_score
261
  }
@@ -270,7 +287,7 @@ class AnonymizationBenchmark:
270
  overall_f1 = 2 * (overall_precision * overall_recall) / (overall_precision + overall_recall) if (overall_precision + overall_recall) > 0 else 0
271
  accuracy = total_tp / total_original_entities if total_original_entities > 0 else 0
272
 
273
- print(f"\n🎯 متریک‌های کلی:")
274
  print(f" Precision: {overall_precision:.4f}")
275
  print(f" Recall: {overall_recall:.4f}")
276
  print(f" F1-Score: {overall_f1:.4f}")
@@ -283,13 +300,9 @@ class AnonymizationBenchmark:
283
  'overall_metrics': {
284
  'total_original_entities': total_original_entities,
285
  'total_anonymized_entities': total_anonymized_codes,
286
- 'total_tp': total_tp,
287
- 'total_fp': total_fp,
288
- 'total_fn': total_fn,
289
- 'precision': overall_precision,
290
- 'recall': overall_recall,
291
- 'f1_score': overall_f1,
292
- 'accuracy': accuracy
293
  }
294
  }
295
 
@@ -309,13 +322,13 @@ class AnonymizationBenchmark:
309
  if df is None:
310
  return "❌ خطا: نمی‌توان فایل را با هیچ encoding خواند"
311
 
312
- print(f"📋 اطلاعات فایل CSV:")
313
  print(f" تعداد ردیف‌ها: {len(df)}")
314
  print(f" ستون‌ها: {df.columns.tolist()}")
315
 
316
  # بررسی ستون‌ها
317
  if 'original_text' not in df.columns or 'anonymized_text' not in df.columns:
318
- return f"❌ خطا: فایل باید شامل ستون‌های 'original_text' و 'anonymized_text' باشد. ستون‌های موجود: {df.columns.tolist()}"
319
 
320
  if len(df) == 0:
321
  return "❌ خطا: فایل خالی است"
@@ -335,7 +348,7 @@ class AnonymizationBenchmark:
335
  continue
336
 
337
  # تحلیل دقیق
338
- analysis = self.analyze_single_row(original_text, anonymized_text)
339
  all_analysis.append(analysis)
340
 
341
  # ذخیره نتیجه
@@ -373,13 +386,14 @@ def process_uploaded_file(file):
373
  result = benchmark.process_csv(file.name)
374
 
375
  if isinstance(result, str):
376
- # خطا رخ داده
377
  return result, None
378
 
379
  results_df, all_analysis = result
380
 
381
  # تولید گزارش نهایی
382
  total_rows = len(results_df)
 
 
383
  avg_precision = results_df['precision'].mean() if 'precision' in results_df.columns else 0
384
  avg_recall = results_df['recall'].mean() if 'recall' in results_df.columns else 0
385
  avg_f1 = results_df['f1_score'].mean() if 'f1_score' in results_df.columns else 0
@@ -393,17 +407,17 @@ def process_uploaded_file(file):
393
 
394
  # گزارش نهایی
395
  report = f"""
396
- {'='*60}
397
- 📊 گزارش نهایی Benchmark
398
- {'='*60}
399
 
400
  📈 آمار کلی:
401
  • تعداد ردیف‌های پردازش شده: {total_rows}
402
- • مجموع Entities اصلی: {total_original}
403
  • مجموع کدهای ناشناس‌سازی: {total_anonymized}
404
- • True Positives: {total_tp}
405
- • False Positives: {total_fp}
406
- • False Negatives: {total_fn}
407
 
408
  🎯 متریک‌های میانگین:
409
  • Precision: {avg_precision:.4f}
@@ -411,7 +425,10 @@ def process_uploaded_file(file):
411
  • F1-Score: {avg_f1:.4f}
412
  • Accuracy: {avg_accuracy:.4f}
413
 
414
- 📋 جزئیات هر ردیف در جدول زیر نمایش داده شده است.
 
 
 
415
  """
416
 
417
  # ذخیره نتایج
@@ -423,7 +440,7 @@ def process_uploaded_file(file):
423
 
424
  # ستون‌های مهم برای نمایش
425
  display_columns = ['row_id', 'total_original_entities', 'total_anonymized_entities',
426
- 'tp', 'fp', 'fn', 'precision', 'recall', 'f1_score', 'accuracy']
427
 
428
  display_df = results_df[[col for col in display_columns if col in results_df.columns]]
429
 
@@ -437,25 +454,25 @@ def download_results():
437
 
438
  # ===== رابط اصلی =====
439
  def main():
440
- with gr.Blocks(title="Benchmark System", theme=gr.themes.Soft()) as demo:
441
 
442
  gr.HTML("""
443
  <h1 style='text-align: center; color: #2E86AB; margin-bottom: 30px;'>
444
- 🎯 سیستم Benchmark ناشناس‌سازی - پردازش فایل آپلودی
445
  </h1>
446
  """)
447
 
448
  with gr.Row():
449
  with gr.Column():
450
  gr.HTML("""
451
- <div style='background: #e8f4fd; padding: 15px; border-radius: 10px; margin-bottom: 15px;'>
452
- <h3>📋 نحوه کارکرد:</h3>
453
- <ol>
454
- <li><b>ستون اول (original_text)</b>: سیستم تمام entities را پیدا می‌کند</li>
455
- <li><b>ستون دوم (anonymized_text)</b>: کدهای ناشناس‌سازی را پیدا می‌کند</li>
456
- <li><b>مقایسه</b>: متریک‌های Precision, Recall, F1-Score, Accuracy محاسبه می‌شود</li>
457
- <li><b>نتیجه</b>: تحلیل دقیق هر ردیف نمایش داده می‌شود</li>
458
- </ol>
459
  </div>
460
  """)
461
 
@@ -465,16 +482,16 @@ def main():
465
  file_count="single"
466
  )
467
 
468
- process_btn = gr.Button("🚀 شروع پردازش فایل آپلودی", variant="primary", size="lg")
469
 
470
  with gr.Row():
471
  with gr.Column():
472
- gr.HTML("<h3>📊 نتایج تحلیل</h3>")
473
 
474
  results_output = gr.Textbox(
475
- label="گزارش کامل",
476
- lines=25,
477
- max_lines=30,
478
  interactive=False
479
  )
480
 
@@ -483,14 +500,14 @@ def main():
483
  gr.HTML("<h3>📋 جدول نتایج</h3>")
484
 
485
  results_table = gr.Dataframe(
486
- label="متریک‌های هر ردیف",
487
  interactive=False,
488
  wrap=True
489
  )
490
 
491
  with gr.Row():
492
  with gr.Column():
493
- download_btn = gr.Button("💾 دانلود نتایج", variant="secondary")
494
  download_file = gr.File(label="فایل نتایج", visible=False)
495
 
496
  # Event handlers
 
1
  #!/usr/bin/env python3
2
  # -*- coding: utf-8 -*-
3
  """
4
+ سیستم benchmark ناشناس‌سازی - ورژن اصلاح شده با الگوهای دقیق
5
  """
6
 
7
  import pandas as pd
 
20
 
21
  # ===== تابع کمکی برای تبدیل numpy/pandas types =====
22
  def convert_to_serializable(obj):
 
23
  if isinstance(obj, (np.integer, np.int64, np.int32)):
24
  return int(obj)
25
  elif isinstance(obj, (np.floating, np.float64, np.float32)):
 
33
  else:
34
  return obj
35
 
36
+ # ===== کلاس پردازش entities با الگوهای دقیق =====
37
  class EntityExtractor:
38
  def __init__(self):
39
+ # الگوهای دقیق‌تر با lookahead و lookbehind
40
  self.patterns = {
41
  'COMPANY': [
42
+ r'شرکت\s+پتروشیمی\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]+?(?=\s|$|،|\.)',
43
+ r'شرکت\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]{2,30}?(?=\s+(?:با|در|که|مربوط)|$|،|\.)',
44
+ r'بانک\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]{2,20}?(?=\s+(?:با|در|کهرائه|صادر)|$|،|\.)',
45
+ r'شرکت\s+تولیدی\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]{2,20}',
46
+ r'شرکت\s+پردازش\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]{2,30}',
47
  ],
48
 
49
  'LOCATION': [
50
+ r'بندر\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]{2,15}',
 
 
51
  r'\b(?:تهران|اصفهان|ماهشهر|عسلویه|بندرعباس|اهواز|شیراز|مشهد|تبریز|کرج|قم|رشت|کرمان|یزد|زاهدان|بوشهر|خرمشهر|آبادان|اراک|قزوین|خوزستان)\b',
52
+ r'جمهوری\s+اسلامی\s+ایران',
 
53
  ],
54
 
55
  'PERSON': [
56
+ r'آقای\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]{2,25}?(?=\s+با\s+کد|\s+مدیر|$|،|\.)',
57
+ r'خانم\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]{2,25}?(?=\s+با\s+کد|\s+همسر|$|،|\.)',
58
+ r'(?:[\u0600-\u06FF\u0750-\u077F\s\u200C]{2,25})\s+مدیرعامل',
59
+ r'مدیر\s+مالی\s+[\u0600-\u06FF\u0750-\u077F\s\u200C]{2,25}',
 
 
 
 
 
60
  ],
61
 
62
  'DATE': [
63
+ r'\d{4}\/\d{1,2}\/\d{1,2}',
64
+ r'سال\s+مالی\s+منتهی\s+به\s+[۰-۹\u06F0-\u06F9]{1,2}\s+[\u0600-\u06FF\u0750-\u077F]+\s+[۰-۹\u06F0-\u06F9]{4}',
65
+ r'[۰-۹\u06F0-\u06F9]{1,2}\s+[\u0600-\u06FF\u0750-\u077F]+\s+[��-۹\u06F0-\u06F9]{4}',
 
 
 
 
66
  ],
67
 
68
  'PHONE': [
69
+ r'09\d{9}(?!\d)', # شماره موبایل
70
+ r'021-\d{8}', # تهران
71
+ r'0\d{2,3}-\d{7,8}', # سایر شهرها
 
72
  ],
73
 
74
  'EMAIL': [
 
76
  ],
77
 
78
  'AMOUNT': [
79
+ r'\d{6,}\s*تومان', # مبالغ 6 رقمی یا بیشتر
80
+ r'مبلغ\s+\d{6,}(?:\s*تومان)?',
81
+ r'\d+\s*درصد',
 
 
82
  ],
83
 
84
  'ACCOUNT': [
85
+ r'\d{3}-\d{3}-\d{3}-\d', # شماره حساب
86
+ r'\d{4}-\d{4}-\d{4}-\d{4}', # شماره کارت
87
+ ],
88
+
89
+ 'ID_NUMBER': [
90
+ r'کد\s+ملی\s+\d{10}',
91
+ r'شناسه\s+ملی\s+\d{11}',
92
+ r'(?<!\d)\d{10}(?!\d)', # کد ملی 10 رقمی مستقل
93
+ r'(?<!\d)\d{11}(?!\d)', # شناسه 11 رقمی مستقل
94
+ ],
95
+
96
+ 'DOCUMENT_NUMBER': [
97
+ r'فاکتور\s+شماره\s+[A-Z0-9-]+',
98
+ r'چک\s+شماره\s+\d+',
99
+ r'گزارش\s+شماره\s+[A-Z0-9-]+',
100
  ]
101
  }
102
 
103
  def clean_entity(self, text):
104
  """تمیز کردن entity استخراج شده"""
105
  # حذف کلمات اضافی در انتها
106
+ text = re.sub(r'\s*(در|که|با|به|از|را|و|یا|است|بوده|نموده|صادر|ارائه|معرفی)\s*$', '', text, flags=re.IGNORECASE)
107
  # حذف فاصله‌های اضافی
108
  text = re.sub(r'\s+', ' ', text).strip()
109
  return text
 
113
  if len(text) < 2:
114
  return False
115
 
116
+ # کلمات ممنوع که نباید به تنهایی entity باشند
117
+ forbidden_alone = ['شد', 'کرد', 'است', 'بود', 'در', 'که', 'با', 'از', 'به', 'را', 'و', 'یا',
118
+ 'شرکت', 'بانک', 'آقای', 'خانم', 'تومان', 'مبلغ']
119
+
120
+ if text.lower().strip() in forbidden_alone:
121
+ return False
122
+
123
+ # بررسی طول منطقی
124
+ if len(text) > 50: # خیلی طولانی است
125
  return False
126
 
127
  return True
 
188
  def __init__(self):
189
  self.extractor = EntityExtractor()
190
 
191
+ def analyze_single_row(self, original_text, anonymized_text, row_number):
192
+ """تحلیل یک ردیف از CSV با نمایش کامل entities"""
193
+ print(f"\n{'='*80}")
194
+ print(f"تحلیل ردیف {row_number}")
195
+ print(f"{'='*80}")
196
 
197
+ print(f"\n📝 متن اصلی ({len(original_text)} کاراکتر):")
198
+ # نمایش 200 کاراکتر اول
199
+ print(f"'{original_text[:200]}{'...' if len(original_text) > 200 else ''}'")
200
 
201
+ print(f"\n🔒 متن ناشناس‌سازی شده ({len(anonymized_text)} کاراکتر):")
202
+ print(f"'{anonymized_text[:200]}{'...' if len(anonymized_text) > 200 else ''}'")
 
203
 
204
  # استخراج entities از متن اصلی
205
+ print(f"\n🔍 Entities استخراج شده از متن اصلی:")
206
  original_entities = self.extractor.extract_entities(original_text)
207
 
208
  total_original_entities = 0
209
  for category, entities in original_entities.items():
210
+ print(f"\n 📊 {category} ({len(entities)} عدد):")
211
  for i, entity in enumerate(entities, 1):
212
  print(f" {i}. '{entity}'")
213
  total_original_entities += len(entities)
214
 
215
  if not original_entities:
216
+ print(" هیچ entity ای یافت نشد!")
217
  else:
218
  print(f"\n✅ مجموع entities یافت شده: {total_original_entities}")
219
 
220
  # استخراج کدهای ناشناس‌سازی
221
+ print(f"\n🔍 کدهای ناشناس‌سازی استخراج شده:")
222
  anonymized_codes = self.extractor.extract_anonymized_codes(anonymized_text)
223
 
224
  total_anonymized_codes = 0
225
  for category, codes in anonymized_codes.items():
226
+ print(f"\n 🔒 {category} ({len(codes)} عدد):")
227
  for i, code in enumerate(codes, 1):
228
  print(f" {i}. '{code}'")
229
  total_anonymized_codes += len(codes)
230
 
231
  if not anonymized_codes:
232
+ print(" هیچ کد ناشناس‌سازی یافت نشد!")
233
  else:
234
  print(f"\n✅ مجموع کدهای ناشناس‌سازی: {total_anonymized_codes}")
235
 
 
240
 
241
  all_categories = set(original_entities.keys()) | set(anonymized_codes.keys())
242
 
243
+ if not all_categories:
244
+ print(" ⚠️ هیچ category ای برای محاسبه یافت نشد!")
245
+ return {
246
+ 'original_entities': {},
247
+ 'anonymized_codes': {},
248
+ 'category_metrics': {},
249
+ 'overall_metrics': {
250
+ 'total_original_entities': 0,
251
+ 'total_anonymized_entities': 0,
252
+ 'total_tp': 0, 'total_fp': 0, 'total_fn': 0,
253
+ 'precision': 0, 'recall': 0, 'f1_score': 0, 'accuracy': 0
254
+ }
255
+ }
256
+
257
  for category in all_categories:
258
  original_count = len(original_entities.get(category, []))
259
  anonymized_count = len(anonymized_codes.get(category, []))
 
266
  recall = tp / (tp + fn) if (tp + fn) > 0 else 0
267
  f1_score = 2 * (precision * recall) / (precision + recall) if (precision + recall) > 0 else 0
268
 
269
+ print(f"\n 📈 {category}:")
270
+ print(f" اصلی: {original_count}, ناشناس‌سازی: {anonymized_count}")
271
  print(f" TP: {tp}, FP: {fp}, FN: {fn}")
272
  print(f" Precision: {precision:.4f}, Recall: {recall:.4f}, F1: {f1_score:.4f}")
273
 
274
  category_metrics[category] = {
275
+ 'original_count': original_count, 'anonymized_count': anonymized_count,
 
276
  'tp': tp, 'fp': fp, 'fn': fn,
277
  'precision': precision, 'recall': recall, 'f1_score': f1_score
278
  }
 
287
  overall_f1 = 2 * (overall_precision * overall_recall) / (overall_precision + overall_recall) if (overall_precision + overall_recall) > 0 else 0
288
  accuracy = total_tp / total_original_entities if total_original_entities > 0 else 0
289
 
290
+ print(f"\n🎯 متریک‌های کلی ردیف {row_number}:")
291
  print(f" Precision: {overall_precision:.4f}")
292
  print(f" Recall: {overall_recall:.4f}")
293
  print(f" F1-Score: {overall_f1:.4f}")
 
300
  'overall_metrics': {
301
  'total_original_entities': total_original_entities,
302
  'total_anonymized_entities': total_anonymized_codes,
303
+ 'total_tp': total_tp, 'total_fp': total_fp, 'total_fn': total_fn,
304
+ 'precision': overall_precision, 'recall': overall_recall,
305
+ 'f1_score': overall_f1, 'accuracy': accuracy
 
 
 
 
306
  }
307
  }
308
 
 
322
  if df is None:
323
  return "❌ خطا: نمی‌توان فایل را با هیچ encoding خواند"
324
 
325
+ print(f"\n📋 اطلاعات فایل CSV:")
326
  print(f" تعداد ردیف‌ها: {len(df)}")
327
  print(f" ستون‌ها: {df.columns.tolist()}")
328
 
329
  # بررسی ستون‌ها
330
  if 'original_text' not in df.columns or 'anonymized_text' not in df.columns:
331
+ return f"❌ خطا: فایل باید شامل ستون‌های 'original_text' و 'anonymized_text' باشد"
332
 
333
  if len(df) == 0:
334
  return "❌ خطا: فایل خالی است"
 
348
  continue
349
 
350
  # تحلیل دقیق
351
+ analysis = self.analyze_single_row(original_text, anonymized_text, index + 1)
352
  all_analysis.append(analysis)
353
 
354
  # ذخیره نتیجه
 
386
  result = benchmark.process_csv(file.name)
387
 
388
  if isinstance(result, str):
 
389
  return result, None
390
 
391
  results_df, all_analysis = result
392
 
393
  # تولید گزارش نهایی
394
  total_rows = len(results_df)
395
+
396
+ # محاسبه آمار کلی
397
  avg_precision = results_df['precision'].mean() if 'precision' in results_df.columns else 0
398
  avg_recall = results_df['recall'].mean() if 'recall' in results_df.columns else 0
399
  avg_f1 = results_df['f1_score'].mean() if 'f1_score' in results_df.columns else 0
 
407
 
408
  # گزارش نهایی
409
  report = f"""
410
+ {'='*80}
411
+ 🎯 گزارش نهایی Benchmark فایل شما
412
+ {'='*80}
413
 
414
  📈 آمار کلی:
415
  • تعداد ردیف‌های پردازش شده: {total_rows}
416
+ • مجموع Entities اصلی در همه ردیف‌ها: {total_original}
417
  • مجموع کدهای ناشناس‌سازی: {total_anonymized}
418
+ • True Positives (درست شناسایی شده): {total_tp}
419
+ • False Positives (اشتباه شناسایی شده): {total_fp}
420
+ • False Negatives (از دست رفته): {total_fn}
421
 
422
  🎯 متریک‌های میانگین:
423
  • Precision: {avg_precision:.4f}
 
425
  • F1-Score: {avg_f1:.4f}
426
  • Accuracy: {avg_accuracy:.4f}
427
 
428
+ 📊 توضیحات:
429
+ • این آمار بر اساس entities واقعی موجود در فایل شما محاسبه شده
430
+ • جزئیات کامل هر ردیف در بالا نمایش داده شده
431
+ • فایل نتایج کامل در جدول زیر قابل مشاهده است
432
  """
433
 
434
  # ذخیره نتایج
 
440
 
441
  # ستون‌های مهم برای نمایش
442
  display_columns = ['row_id', 'total_original_entities', 'total_anonymized_entities',
443
+ 'total_tp', 'total_fp', 'total_fn', 'precision', 'recall', 'f1_score', 'accuracy']
444
 
445
  display_df = results_df[[col for col in display_columns if col in results_df.columns]]
446
 
 
454
 
455
  # ===== رابط اصلی =====
456
  def main():
457
+ with gr.Blocks(title="Precise Benchmark System", theme=gr.themes.Soft()) as demo:
458
 
459
  gr.HTML("""
460
  <h1 style='text-align: center; color: #2E86AB; margin-bottom: 30px;'>
461
+ 🎯 سیستم Benchmark دقیق ناشناس‌سازی
462
  </h1>
463
  """)
464
 
465
  with gr.Row():
466
  with gr.Column():
467
  gr.HTML("""
468
+ <div style='background: #e8f5e8; padding: 15px; border-radius: 10px; margin-bottom: 15px;'>
469
+ <h3> ویژگی‌های جدید:</h3>
470
+ <ul>
471
+ <li><b>الگوهای دقیق:</b> entities را دقیق‌تر شناسایی می‌کند</li>
472
+ <li><b>نمایش کامل:</b> تمام entities پیدا شده لیست می‌شود</li>
473
+ <li><b>تحلیل مفصل:</b> هر ردیف جداگانه تحلیل می‌شود</li>
474
+ <li><b>محاسبه دقیق:</b> متریک‌های صحیح بر اساس entities واقعی</li>
475
+ </ul>
476
  </div>
477
  """)
478
 
 
482
  file_count="single"
483
  )
484
 
485
+ process_btn = gr.Button("🚀 تحلیل فایل با نمایش کامل Entities", variant="primary", size="lg")
486
 
487
  with gr.Row():
488
  with gr.Column():
489
+ gr.HTML("<h3>📊 گزارش کامل + نمایش Entities</h3>")
490
 
491
  results_output = gr.Textbox(
492
+ label="گزارش شامل لیست کامل entities پیدا شده",
493
+ lines=30,
494
+ max_lines=35,
495
  interactive=False
496
  )
497
 
 
500
  gr.HTML("<h3>📋 جدول نتایج</h3>")
501
 
502
  results_table = gr.Dataframe(
503
+ label="خلاصه متریک‌های هر ردیف",
504
  interactive=False,
505
  wrap=True
506
  )
507
 
508
  with gr.Row():
509
  with gr.Column():
510
+ download_btn = gr.Button("💾 دانلود نتایج کامل", variant="secondary")
511
  download_file = gr.File(label="فایل نتایج", visible=False)
512
 
513
  # Event handlers