leilaghomashchi commited on
Commit
d6c9adc
·
verified ·
1 Parent(s): 4bc3298

Upload fixed_anonymizer (2).py

Browse files
Files changed (1) hide show
  1. fixed_anonymizer (2).py +671 -0
fixed_anonymizer (2).py ADDED
@@ -0,0 +1,671 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ سیستم ناشناس‌سازی اصلاح شده - حل مشکلات آدرس کامل و شناسایی دقیق‌تر
5
+ """
6
+
7
+ import gradio as gr
8
+ import re
9
+ import os
10
+ import requests
11
+ import time
12
+ import logging
13
+
14
+ logging.basicConfig(level=logging.INFO)
15
+ logger = logging.getLogger(__name__)
16
+
17
+ class ImprovedDataAnonymizer:
18
+ def __init__(self):
19
+ self.mapping_table = {}
20
+ self.pattern_categories = {
21
+ 'personal_identity': {
22
+ 'name_fa': 'اطلاعات شخصی و هویتی',
23
+ 'name_en': 'Personal & Identity Information',
24
+ 'patterns': ['PERSON', 'ID_NUMBER', 'MIXED_NAMES'],
25
+ 'icon': '👤'
26
+ },
27
+ 'financial': {
28
+ 'name_fa': 'اطلاعات مالی',
29
+ 'name_en': 'Financial Information',
30
+ 'patterns': ['AMOUNT', 'ACCOUNT', 'CARD_NUMBER'],
31
+ 'icon': '💰'
32
+ },
33
+ 'temporal': {
34
+ 'name_fa': 'اطلاعات زمانی',
35
+ 'name_en': 'Temporal Information',
36
+ 'patterns': ['DATE'],
37
+ 'icon': '📅'
38
+ },
39
+ 'location': {
40
+ 'name_fa': 'اطلاعات مکانی',
41
+ 'name_en': 'Location Information',
42
+ 'patterns': ['LOCATION', 'FULL_ADDRESS'],
43
+ 'icon': '📍'
44
+ },
45
+ 'business': {
46
+ 'name_fa': 'اطلاعات کسب‌وکار',
47
+ 'name_en': 'Business Information',
48
+ 'patterns': ['COMPANY', 'BRANCH'],
49
+ 'icon': '🏢'
50
+ },
51
+ 'communication': {
52
+ 'name_fa': 'اطلاعات ارتباطی',
53
+ 'name_en': 'Communication Information',
54
+ 'patterns': ['PHONE', 'EMAIL'],
55
+ 'icon': '📞'
56
+ }
57
+ }
58
+
59
+ self.counters = {
60
+ 'PERSON': 0, 'ID_NUMBER': 0, 'MIXED_NAMES': 0,
61
+ 'AMOUNT': 0, 'ACCOUNT': 0, 'CARD_NUMBER': 0,
62
+ 'DATE': 0,
63
+ 'LOCATION': 0, 'FULL_ADDRESS': 0,
64
+ 'COMPANY': 0, 'BRANCH': 0,
65
+ 'PHONE': 0, 'EMAIL': 0
66
+ }
67
+
68
+ self.api_key = os.getenv("OPENAI_API_KEY", "")
69
+
70
+ def get_improved_patterns(self):
71
+ """الگوهای بهبود یافته با حل مشکلات شناسایی آدرس کامل"""
72
+ return {
73
+ # آدرس‌های کامل - اولویت بالا برای گرفتن آدرس کامل قبل از قطعات
74
+ 'FULL_ADDRESS': [
75
+ # الگوی آدرس کامل: شهر + خیابان + کوچه + پلاک + طبقه
76
+ r'(?:تهران|اصفهان|مشهد|شیراز|کرج|اهواز|قم|رشت|کرمان|یزد|بوشهر|ارومیه|همدان|بندر عباس|ساری|اردبیل|خرم‌آباد|ایلام|بیرجند|گرگان|زنجان|سنندج|شهرکرد|سبزوار|قزوین|زاهدان|خوی|مراغه|کاشان|نجف‌آباد|شاهین‌شهر|ملایر|آبادان|دزفول|بابل|آمل|شاهرود|گنبد کاووس|خرمشهر|جهرم|فسا|مرودشت|لار|داراب|فیروزآباد|کازرون|سپیدان|نی‌ریز|استهبان|فارسان|میانه|ورامین|قرچک|ری|پاکدشت|دماوند|فیروزکوه|شهریار|اسلام‌شهر|ملارد|قدس|بهارستان|چهاردانگه)،\s*(?:خیابان|کوچه|شهرک|بلوار|میدان|کوی|محله)\s+[آ-ی‌ّٰ-ٹ\s]+(?:،\s*(?:خیابان|کوچه|بلوار|کوی)\s+[آ-ی‌ّٰ-ٹ\s]+)?(?:،\s*پلاک\s+\d+)?(?:،\s*(?:طبقه|واحد)\s+[آ-ی‌ّٰ-ٹ\d\s]+)?',
77
+
78
+ # الگوی آدرس با شهرک
79
+ r'(?:تهران|اصفهان|مشهد|شیراز|کرج|اهواز|قم|رشت|کرمان|یزد|بوشهر|ارومیه|همدان|بندر عباس|ساری|اردبیل|خرم‌آباد|ایلام|بیرجند|گرگان|زنجان|سنندج|شهرکرد|سبزوار|قزوین|زاهدان|خوی|مراغه|کاشان|نجف‌آباد|شاهین‌شهر|ملایر|آبادان|دزفول|بابل|آمل|شاهرود|گنبد کاووس|خرمشهر|جهرم|فسا|مرودشت|لار|داراب|فیروزآباد|کازرون|سپیدان|نی‌ریز|استهبان|فارسان|میانه|ورامین|قرچک|ری|پاکدشت|دماوند|فیروزکوه|شهریار|اسلام‌شهر|ملارد|قدس|بهارستان|چهاردانگه)،\s*شهرک\s+[آ-ی‌ّٰ-ٹ\s]+،\s*(?:خیابان|کوچه|بلوار)\s+[آ-ی‌ّٰ-ٹ\s]+(?:،\s*پلاک\s+\d+)?',
80
+
81
+ # الگوی ساده‌تر برای آدرس‌های کوتاه‌تر
82
+ r'خیابان\s+[آ-ی‌ّٰ-ٹ\s]+،\s*کوچه\s+[آ-ی‌ّٰ-ٹ\s]+،\s*پلاک\s+\d+(?:،\s*(?:طبقه|واحد)\s+[آ-ی‌ّٰ-ٹ\d\s]+)?',
83
+ ],
84
+
85
+ # اسامی اشخاص - الگوهای دقیق‌تر
86
+ 'PERSON': [
87
+ r'آقای\s+[آ-ی‌ّٰ-ٹ]+\s+[آ-ی‌ّٰ-ٹ]+(?=\s+با\s+کد|\s+مدیر|$|،|\.)',
88
+ r'خانم\s+[آ-ی‌ّٰ-ٹ]+\s+[آ-ی‌ّٰ-ٹ]+(?=\s+با\s+کد|\s+همسر|$|،|\.)',
89
+ r'مهندس\s+[آ-ی‌ّٰ-ٹ]+\s+[آ-ی‌ّٰ-ٹ]+(?=\s+با\s+کد|$|،|\.)',
90
+ r'دکتر\s+[آ-ی‌ّٰ-ٹ]+\s+[آ-ی‌ّٰ-ٹ]+(?=\s+با\s+کد|$|،|\.)',
91
+ r'Mr\.\s+[A-Z][a-z]+\s+[A-Z][a-z]+(?=\s|,|\.|$)',
92
+ r'Ms\.\s+[A-Z][a-z]+\s+[A-Z][a-z]+(?=\s|,|\.|$)',
93
+ r'Dr\.\s+[A-Z][a-z]+\s+[A-Z][a-z]+(?=\s|,|\.|$)',
94
+ ],
95
+
96
+ # کدهای ملی و شناسه‌ها - جداسازی از شماره تلفن
97
+ 'ID_NUMBER': [
98
+ r'کد\s+ملی\s+\d{10}',
99
+ r'شناسه\s+ملی\s+\d{11}',
100
+ r'(?<!09)(?<![0-9])\d{10}(?![0-9])', # کد ملی 10 رقمی مستقل (نه شماره موبایل)
101
+ r'(?<!09)(?<![0-9])\d{11}(?![0-9])', # شناسه 11 رقمی مستقل (نه شماره موبایل)
102
+ ],
103
+
104
+ # مبالغ مالی - جداسازی از شماره تلفن
105
+ 'AMOUNT': [
106
+ r'\d{6,}\s*تومان', # مبالغ 6 رقمی یا بیشتر با کلمه تومان
107
+ r'مبلغ\s+\d{6,}(?:\s*تومان)?',
108
+ r'موجودی\s+حساب\s+[^\s]+\s+حدود\s+\d{6,}\s*تومان',
109
+ r'ارزش\s+روز\s+آن\s+\d{6,}\s*تومان',
110
+ r'میانگین\s+موجودی\s+حساب\s+وی\s+حدود\s+\d{6,}\s*تومان',
111
+ r'\$\d+(?:,\d{3})*(?:\.\d+)?',
112
+ ],
113
+
114
+ # شماره حساب و کارت بانکی - جداسازی دقیق
115
+ 'ACCOUNT': [
116
+ r'حساب\s+جاری\s+شماره\s+[\d-]+',
117
+ r'شماره\s+[\d-]{8,}(?=\s+در)', # شماره حساب
118
+ ],
119
+
120
+ 'CARD_NUMBER': [
121
+ r'شماره\s+\d{4}-\d{4}-\d{4}-\d{4}', # شماره کارت
122
+ r'\d{4}-\d{4}-\d{4}-\d{4}(?=\s+نیز)',
123
+ ],
124
+
125
+ # شماره تلفن - دقیق‌تر شده
126
+ 'PHONE': [
127
+ r'شماره\s+تماس\s+09\d{9}',
128
+ r'(?<![0-9])09\d{9}(?![0-9])', # شماره موبایل مستقل
129
+ r'تلفن[\s:]*0\d{2,3}[-\s]?\d{7,8}',
130
+ ],
131
+
132
+ # تاریخ
133
+ 'DATE': [
134
+ r'\d{4}/\d{1,2}/\d{1,2}',
135
+ r'[۰-۹]{1,2}\s+(?:فروردین|اردیبهشت|خرداد|تیر|مرداد|شهریور|مهر|آبان|آذر|دی|بهمن|اسفند)\s+[۰-۹]{4}',
136
+ ],
137
+
138
+ # شرکت‌ها - حذف الگوهای عمومی
139
+ 'COMPANY': [
140
+ r'شرکت\s+[آ-ی‌ّٰ-ٹ\s]+(?=\s|$|،|\.)',
141
+ r'بانک\s+[آ-ی‌ّٰ-ٹ\s]+(?=\s|$|،|\.)',
142
+ r'[A-Z][a-zA-Z\s]+(?:Inc|Corp|Corporation|Company|Ltd|Limited|LLC)',
143
+ ],
144
+
145
+ # شعب و واحدهای تجاری - دقیق‌تر شده
146
+ 'BRANCH': [
147
+ r'شعبه\s+[آ-ی‌ّٰ-ٹ\s]+\s+بانک\s+[آ-ی‌ّٰ-ٹ\s]+',
148
+ r'شعبه\s+مرکزی\s+بانک\s+[آ-ی‌ّٰ-ٹ\s]+',
149
+ ],
150
+
151
+ # مکان‌ها - شهرها (فقط نام شهرها)
152
+ 'LOCATION': [
153
+ r'\b(?:تهران|اصفهان|مشهد|شیراز|کرج|اهواز|قم|رشت|کرمان|یزد|بوشهر|ارومیه|همدان|بندر عباس|ساری|اردبیل|خرم‌آباد|ایلام|بیرجند|گرگان|زنجان|سنندج|شهرکرد|سبزوار|قزوین|زاهدان|خوی|مراغه|کاشان|نجف‌آباد|شاهین‌شهر|ملایر|آبادان|دزفول|بابل|آمل|شاهرود|گنبد کاووس|خرمشهر|جهرم|فسا|مرودشت|لار|داراب|فیروزآباد|کازرون|سپیدان|نی‌ریز|استهبان|فارسان|میانه|ورامین|قرچک|ری|پاکدشت|دماوند|فیروزکوه|شهریار|اسلام‌شهر|ملارد|قدس|بهارستان|چهاردانگه)\b(?!\s*،)', # فقط نام شهر تنها
154
+ r'استان\s+[آ-ی‌ّٰ-ٹ\s]+',
155
+ r'شهر\s+[آ-ی‌ّٰ-ٹ\s]+',
156
+ ],
157
+
158
+ # ایمیل
159
+ 'EMAIL': [
160
+ r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}',
161
+ ],
162
+ }
163
+
164
+ def get_category_choices(self, language='fa'):
165
+ """دریافت لیست دسته‌بندی‌ها برای چک‌باکس"""
166
+ choices = []
167
+ for cat_key, cat_info in self.pattern_categories.items():
168
+ name = cat_info['name_fa'] if language == 'fa' else cat_info['name_en']
169
+ icon = cat_info['icon']
170
+ choices.append(f"{icon} {name}")
171
+ return choices
172
+
173
+ def get_selected_patterns(self, selected_categories, language='fa'):
174
+ """تبدیل دسته‌بندی‌های انتخاب شده به لیست الگوها"""
175
+ selected_patterns = []
176
+
177
+ for cat_key, cat_info in self.pattern_categories.items():
178
+ name = cat_info['name_fa'] if language == 'fa' else cat_info['name_en']
179
+ icon = cat_info['icon']
180
+ category_display = f"{icon} {name}"
181
+
182
+ if category_display in selected_categories:
183
+ selected_patterns.extend(cat_info['patterns'])
184
+
185
+ return selected_patterns
186
+
187
+ def anonymize_text(self, original_text, lang='fa', selected_categories=None):
188
+ """ناشناس‌سازی متن با الگوهای بهبود یافته"""
189
+ try:
190
+ if not original_text or not original_text.strip():
191
+ return "⚠ Please enter input text!" if lang == 'en' else "⚠ لطفاً متن ورودی را وارد کنید!"
192
+
193
+ # ریست متغیرها
194
+ self.mapping_table = {}
195
+ self.counters = {key: 0 for key in self.counters.keys()}
196
+
197
+ anonymized = original_text
198
+ found_entities = set()
199
+
200
+ # استخراج با الگوهای بهبود یافته
201
+ patterns = self.get_improved_patterns()
202
+
203
+ # فیلتر کردن الگوها بر اساس انتخاب کاربر
204
+ if selected_categories:
205
+ selected_pattern_types = self.get_selected_patterns(selected_categories, lang)
206
+ patterns = {k: v for k, v in patterns.items() if k in selected_pattern_types}
207
+
208
+ logger.info("🔍 Running improved regex extraction with full address detection...")
209
+
210
+ processed_entities = set()
211
+
212
+ # ترتیب اولویت بهبود یافته - آدرس کامل در اولویت بالا
213
+ priority_order = [
214
+ 'FULL_ADDRESS', # بالاترین اولویت - آدرس کامل قبل از قطعات
215
+ 'EMAIL',
216
+ 'ID_NUMBER',
217
+ 'CARD_NUMBER',
218
+ 'ACCOUNT',
219
+ 'PHONE',
220
+ 'AMOUNT',
221
+ 'BRANCH',
222
+ 'COMPANY',
223
+ 'LOCATION', # پایین‌تر از آدرس کامل
224
+ 'DATE',
225
+ 'PERSON',
226
+ 'MIXED_NAMES'
227
+ ]
228
+
229
+ for category in priority_order:
230
+ if category in patterns:
231
+ pattern_list = patterns[category]
232
+ for pattern in pattern_list:
233
+ try:
234
+ matches = re.finditer(pattern, original_text, re.IGNORECASE | re.MULTILINE)
235
+ for match in matches:
236
+ full_match = match.group(0).strip()
237
+
238
+ # بررسی تداخل
239
+ overlaps = False
240
+ match_start, match_end = match.span()
241
+
242
+ for proc_start, proc_end in processed_entities:
243
+ if not (match_end <= proc_start or match_start >= proc_end):
244
+ overlaps = True
245
+ break
246
+
247
+ # شرایط قبولی - با فیلتر کلمات عمومی
248
+ if (not overlaps and
249
+ full_match not in found_entities and
250
+ full_match not in self.mapping_table and
251
+ len(full_match) >= 3 and
252
+ not full_match.isspace() and
253
+ not self._is_generic_word(full_match)): # فیلتر کلمات عمومی
254
+
255
+ self.counters[category] += 1
256
+ code = f"{category.lower()}_{self.counters[category]:03d}"
257
+ self.mapping_table[full_match] = code
258
+ found_entities.add(full_match)
259
+ processed_entities.add((match_start, match_end))
260
+
261
+ except re.error as e:
262
+ logger.error(f"Regex error in pattern {pattern}: {e}")
263
+ continue
264
+
265
+ # جایگزینی در متن - طولانی‌ترین‌ها اول
266
+ sorted_items = sorted(self.mapping_table.items(), key=lambda x: len(x[0]), reverse=True)
267
+ for original_item, code in sorted_items:
268
+ anonymized = anonymized.replace(original_item, code)
269
+
270
+ logger.info(f"✅ Improved anonymization completed. Found {len(self.mapping_table)} entities.")
271
+ return anonymized
272
+
273
+ except Exception as e:
274
+ logger.error(f"Anonymization error: {e}")
275
+ return f"⚠ Error in anonymization: {str(e)}" if lang == 'en' else f"⚠ خطا در ناشناس‌سازی: {str(e)}"
276
+
277
+ def _is_generic_word(self, text):
278
+ """بررسی کلمات عمومی که نباید entity محسوب شوند"""
279
+ generic_words = {
280
+ 'همین بانک', 'این بانک', 'آن بانک', 'بانک مذکور',
281
+ 'همین شرکت', 'این شرکت', 'آن شرکت', 'شرکت مذکور',
282
+ 'همین شعبه', 'این شعبه', 'آن شعبه', 'شعبه مذکور',
283
+ 'همین شهر', 'این شهر', 'آن شهر',
284
+ 'متقاضی', 'ایشان', 'وی', 'مشتری',
285
+ 'بانک', 'شرکت', 'شعبه' # کلمات تنها
286
+ }
287
+ return text.strip() in generic_words or len(text.strip()) < 3
288
+
289
+ def send_to_chatgpt(self, anonymized_text, lang='fa'):
290
+ """ارسال به ChatGPT"""
291
+ try:
292
+ if not anonymized_text or not anonymized_text.strip():
293
+ return "⚠ Anonymized text is empty!" if lang == 'en' else "⚠ متن ناشناس‌شده خالی است!"
294
+
295
+ if not self.api_key:
296
+ return "⚠ API Key not configured!" if lang == 'en' else "⚠ کلید API تنظیم نشده است!"
297
+
298
+ system_msg = "You are a professional analyst. Answer questions accurately." if lang == 'en' else "شما یک تحلیل‌گر حرفه‌ای هستید. به سوالات با دقت پاسخ دهید."
299
+
300
+ headers = {
301
+ "Authorization": f"Bearer {self.api_key}",
302
+ "Content-Type": "application/json"
303
+ }
304
+
305
+ data = {
306
+ "model": "gpt-4o-mini",
307
+ "messages": [
308
+ {"role": "system", "content": system_msg},
309
+ {"role": "user", "content": anonymized_text}
310
+ ],
311
+ "max_tokens": 2000,
312
+ "temperature": 0.7
313
+ }
314
+
315
+ response = requests.post(
316
+ "https://api.openai.com/v1/chat/completions",
317
+ headers=headers,
318
+ json=data,
319
+ timeout=30
320
+ )
321
+
322
+ if response.status_code == 200:
323
+ result = response.json()
324
+ return result['choices'][0]['message']['content']
325
+ else:
326
+ error_data = response.json() if response.content else {}
327
+ error_message = error_data.get('error', {}).get('message', response.text)
328
+ return f"⚠ API Error: {error_message}"
329
+
330
+ except Exception as e:
331
+ return f"⚠ Error connecting to ChatGPT: {str(e)}" if lang == 'en' else f"⚠ خطا در ارتباط با ChatGPT: {str(e)}"
332
+
333
+ def deanonymize_response(self, gpt_response, lang='fa'):
334
+ """بازگردانی"""
335
+ try:
336
+ if not gpt_response or not gpt_response.strip():
337
+ return "⚠ ChatGPT response is empty!" if lang == 'en' else "⚠ پاسخ ChatGPT خالی است!"
338
+
339
+ if not self.mapping_table:
340
+ return "⚠ Mapping table is empty!" if lang == 'en' else "⚠ جدول نگاشت خالی است!"
341
+
342
+ final_result = gpt_response
343
+ reverse_mapping = {code: original for original, code in self.mapping_table.items()}
344
+
345
+ sorted_codes = sorted(reverse_mapping.items(), key=lambda x: len(x[0]), reverse=True)
346
+ for code, original in sorted_codes:
347
+ final_result = final_result.replace(code, original)
348
+
349
+ return final_result
350
+
351
+ except Exception as e:
352
+ return f"⚠ Deanonymization error: {str(e)}" if lang == 'en' else f"⚠ خطا در بازگردانی: {str(e)}"
353
+
354
+ def get_model_status(self):
355
+ """وضعیت سیستم"""
356
+ status = "🚀 **سیستم ناشناس‌سازی بهبود یافته - نسخه اصلاح شده:**\n\n"
357
+
358
+ status += "• **مشکلات حل شده:**\n"
359
+ status += " ✅ آدرس کامل به عنوان یک entity واحد\n"
360
+ status += " ✅ حذف کلمات عمومی مثل 'همین بانک'\n"
361
+ status += " ✅ اولویت‌بندی بهتر برای آدرس کامل\n"
362
+ status += " ✅ جداسازی دقیق کد ملی از شماره تلفن\n"
363
+ status += " ✅ جداسازی مبالغ مالی از شماره تلفن\n"
364
+ status += " ✅ شماره حساب و کارت بانکی جداگانه\n\n"
365
+
366
+ status += "• **بهبودهای الگو:**\n"
367
+ status += " 🎯 الگوی جامع برای آدرس کامل فارسی\n"
368
+ status += " 🎯 فیلتر کلمات عمومی\n"
369
+ status += " 🎯 اولویت‌بندی بهتر پردازش\n"
370
+ status += " 🎯 حذف تداخل‌های غلط\n\n"
371
+
372
+ status += f"📊 **دسته‌بندی‌های موجود:**\n"
373
+ for cat_key, cat_info in self.pattern_categories.items():
374
+ icon = cat_info['icon']
375
+ name_fa = cat_info['name_fa']
376
+ pattern_count = len(cat_info['patterns'])
377
+ status += f" {icon} {name_fa}: {pattern_count} الگو\n"
378
+
379
+ status += "\n🔧 **اصلاحات اخیر:**\n"
380
+ status += " ✅ آدرس کامل: تهران، خیابان ولیعصر، کوچه نیلوفر، پلاک 128، طبقه سوم → full_address_001\n"
381
+ status += " ✅ آدرس کامل: کرج، شهرک اندیشه، خیابان گلستان، پلاک 45 → full_address_002\n"
382
+ status += " ✅ فیلتر: 'همین بانک' حذف شد\n"
383
+
384
+ return status
385
+
386
+ # ایجاد instance
387
+ anonymizer = ImprovedDataAnonymizer()
388
+
389
+ def process_all_steps(input_text, language, selected_categories):
390
+ """پردازش خودکار تمام مراحل - نسخه اصلاح شده"""
391
+ lang = 'en' if language == 'English' else 'fa'
392
+
393
+ if not input_text.strip():
394
+ error_msg = "⚠ Please enter input text!" if lang == 'en' else "⚠ لطفاً متن ورودی را وارد کنید!"
395
+ return error_msg, "", "", ""
396
+
397
+ try:
398
+ start_time = time.time()
399
+
400
+ anonymized_text = anonymizer.anonymize_text(input_text, lang, selected_categories)
401
+ if anonymized_text.startswith("⚠"):
402
+ return anonymized_text, "", "", ""
403
+
404
+ gpt_response = anonymizer.send_to_chatgpt(anonymized_text, lang)
405
+ if gpt_response.startswith("⚠"):
406
+ entities_found = len(anonymizer.mapping_table)
407
+ selected_count = len(selected_categories) if selected_categories else 0
408
+
409
+ success_msg = (f"✅ ناشناس‌سازی اصلاح شده انجام شد!\n"
410
+ f"📋 دسته‌های انتخابی: {selected_count} | 🔍 پردازش آدرس کامل\n"
411
+ f"📊 کل entities محافظت شده: {entities_found} | 🎯 مشکلات حل شده!")
412
+ return success_msg, anonymized_text, gpt_response, ""
413
+
414
+ final_result = anonymizer.deanonymize_response(gpt_response, lang)
415
+
416
+ total_time = time.time() - start_time
417
+ entities_found = len(anonymizer.mapping_table)
418
+ selected_count = len(selected_categories) if selected_categories else 6
419
+
420
+ success_msg = (f"🎉 ناشناس‌سازی کامل و بازگردانی موفق!\n"
421
+ f"🔧 روش: پردازش اصلاح شده | 📋 دسته‌ها: {selected_count}/6\n"
422
+ f"📊 کل: {entities_found} entities | ⏱️ زمان: {total_time:.2f}s\n"
423
+ f"⚡ مشکلات آدرس کامل حل شدند!")
424
+
425
+ return success_msg, anonymized_text, gpt_response, final_result
426
+
427
+ except Exception as e:
428
+ error_msg = f"⚠ Processing error: {str(e)}" if lang == 'en' else f"⚠ خطا در پردازش: {str(e)}"
429
+ return error_msg, "", "", ""
430
+
431
+ def get_mapping_table(language):
432
+ """نمایش جدول نگاشت"""
433
+ lang = 'en' if language == 'English' else 'fa'
434
+
435
+ if not anonymizer.mapping_table:
436
+ return "⚠ Mapping table is empty!" if lang == 'en' else "⚠ جدول نگاشت خالی است!"
437
+
438
+ result = "📋 **جدول نگاشت اصلاح شده:**\n\n"
439
+
440
+ # نمایش آمار کلی
441
+ result += f"📊 **آمار**: {len(anonymizer.mapping_table)} entity\n"
442
+ result += f"🔍 **روش**: پردازش آدرس کامل اصلاح شده\n"
443
+ result += f"⚡ **حالت**: شناسایی دقیق entities و فیلتر کلمات عمومی\n\n"
444
+
445
+ # دسته‌بندی نتایج
446
+ category_stats = {}
447
+ for original, code in anonymizer.mapping_table.items():
448
+ category = code.split('_')[0].upper()
449
+ if category not in category_stats:
450
+ category_stats[category] = []
451
+ category_stats[category].append((original, code))
452
+
453
+ # نمایش نتایج بر اساس دسته‌بندی
454
+ for category, items in category_stats.items():
455
+ if len(items) > 0:
456
+ result += f"🔍 **{category}** ({len(items)} مورد):\n"
457
+ for original, code in items:
458
+ result += f" • `{original}` → `{code}`\n"
459
+ result += "\n"
460
+
461
+ result += "✨ **سیستم اصلاح شده**: تشخیص آدرس کامل و حذف کلمات عمومی!"
462
+
463
+ return result
464
+
465
+ def clear_all():
466
+ """پاک کردن همه"""
467
+ anonymizer.mapping_table = {}
468
+ anonymizer.counters = {key: 0 for key in anonymizer.counters.keys()}
469
+ return "", "", "", "", ""
470
+
471
+ # رابط کاربری Gradio
472
+ custom_css = """
473
+ body, .gradio-container {
474
+ font-family: 'Segoe UI', Tahoma, Arial, sans-serif !important;
475
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
476
+ min-height: 100vh !important;
477
+ padding: 20px !important;
478
+ }
479
+
480
+ .gradio-textbox {
481
+ border-radius: 10px !important;
482
+ box-shadow: 0 4px 15px rgba(0,0,0,0.1) !important;
483
+ min-height: 300px !important;
484
+ }
485
+
486
+ .status-box {
487
+ background: linear-gradient(135deg, #4CAF50, #45a049) !important;
488
+ border: 3px solid #2E7D32 !important;
489
+ border-radius: 15px !important;
490
+ padding: 15px !important;
491
+ margin: 10px 0 !important;
492
+ box-shadow: 0 8px 32px rgba(76, 175, 80, 0.3) !important;
493
+ }
494
+
495
+ .gradio-button {
496
+ border-radius: 25px !important;
497
+ font-weight: bold !important;
498
+ transition: all 0.3s ease !important;
499
+ margin: 5px 0 !important;
500
+ min-height: 50px !important;
501
+ }
502
+
503
+ h1 {
504
+ background: linear-gradient(45deg, #FFD700, #FFA500) !important;
505
+ -webkit-background-clip: text !important;
506
+ -webkit-text-fill-color: transparent !important;
507
+ background-clip: text !important;
508
+ text-align: center !important;
509
+ }
510
+ """
511
+
512
+ with gr.Blocks(title="🔧 سیستم ناشناس‌سازی اصلاح شده", theme=gr.themes.Soft(), css=custom_css) as app:
513
+
514
+ with gr.Column():
515
+ gr.HTML("<h1>🔧 سیستم ناشناس‌سازی اصلاح شده - حل مشکلات آدرس کامل</h1>")
516
+
517
+ with gr.Row():
518
+ language_selector = gr.Radio(
519
+ choices=["فارسی", "English"],
520
+ value="فارسی",
521
+ label="Language / زبان",
522
+ interactive=True
523
+ )
524
+
525
+ # بخش انتخاب دسته‌بندی‌ها
526
+ with gr.Row():
527
+ with gr.Column():
528
+ gr.HTML("<h3 style='text-align: center; color: #1976D2;'>🎯 انتخاب دسته‌بندی‌های الگو</h3>")
529
+
530
+ pattern_categories = gr.CheckboxGroup(
531
+ choices=anonymizer.get_category_choices('fa'),
532
+ value=anonymizer.get_category_choices('fa'),
533
+ label="انتخاب دسته‌بندی‌های الگو:",
534
+ interactive=True
535
+ )
536
+
537
+ gr.HTML("""
538
+ <div style='background: rgba(255, 255, 255, 0.9); padding: 15px; border-radius: 10px; margin-top: 10px;'>
539
+ <p style='margin: 0; color: #666; font-size: 0.9em; text-align: center;'>
540
+ 🔧 <strong>اصلاحات:</strong> آدرس کامل، حذف کلمات عمومی، اولویت‌بندی بهتر
541
+ </p>
542
+ </div>
543
+ """)
544
+
545
+ with gr.Row():
546
+ with gr.Column(scale=1):
547
+ gr.HTML('<h2>📝 متن ورودی</h2>')
548
+
549
+ input_text = gr.Textbox(
550
+ lines=15,
551
+ placeholder="متن اصلی خود را اینجا وارد کنید...\nمثال متن مشکل‌دار شما:\nآقای علی احمدی با کد ملی 0123456789 در تاریخ 1402/09/15 درخواست تسهیلات بانکی به مبلغ 500000000 تومان از شعبه مرکزی بانک ملی ارائه نموده است. ایشان ساکن تهران، خیابان ولیعصر، کوچه نیلوفر، پلاک 128، طبقه سوم بوده و شماره تماس 09123456789 را اعلام نموده‌اند...\n\n🔧 حالا مشکلات حل شده‌اند!",
552
+ label="",
553
+ rtl=True
554
+ )
555
+
556
+ with gr.Row():
557
+ process_btn = gr.Button("🚀 پردازش با الگوهای اصلاح شده", variant="primary")
558
+ clear_btn = gr.Button("🗑️ پاک کردن همه", variant="stop")
559
+
560
+ status = gr.Textbox(
561
+ label="وضعیت",
562
+ lines=4,
563
+ interactive=False,
564
+ rtl=True,
565
+ elem_classes=["status-box"]
566
+ )
567
+
568
+ with gr.Column(scale=1):
569
+ gr.HTML('<h2>🎭 متن ناشناس‌شده</h2>')
570
+
571
+ anonymized_output = gr.Textbox(
572
+ lines=15,
573
+ placeholder="متن ناشناس‌شده اینجا نمایش داده می‌شود...\nانتظار می‌رود:\n- person_001 به جای آقای علی احمدی\n- full_address_001 به جای تهران، خیابان ولیعصر، کوچه نیلوفر، پلاک 128، طبقه سوم\n- amount_001 به جای 500000000 تومان\n- و سایر موارد...",
574
+ label="",
575
+ interactive=False,
576
+ rtl=True
577
+ )
578
+
579
+ with gr.Row():
580
+ with gr.Column(scale=1):
581
+ gr.HTML('<h2>🤖 پاسخ خام ChatGPT</h2>')
582
+
583
+ gpt_output = gr.Textbox(
584
+ lines=10,
585
+ placeholder="پاسخ خام ChatGPT اینجا نمایش داده می‌شود...",
586
+ label="",
587
+ interactive=False,
588
+ rtl=True
589
+ )
590
+
591
+ with gr.Column(scale=1):
592
+ gr.HTML('<h2>✅ پاسخ نهایی بازگردانده شده</h2>')
593
+
594
+ final_output = gr.Textbox(
595
+ lines=10,
596
+ placeholder="پاسخ نهایی اینجا نمایش داده می‌شود...",
597
+ label="",
598
+ interactive=False,
599
+ rtl=True
600
+ )
601
+
602
+ with gr.Row():
603
+ with gr.Column():
604
+ gr.HTML('<h2>📋 جدول نگاشت</h2>')
605
+ mapping_btn = gr.Button("📋 نمایش جدول نگاشت اصلاح شده")
606
+
607
+ mapping_output = gr.Textbox(
608
+ lines=15,
609
+ label="جدول نگاشت اطلاعات",
610
+ interactive=False,
611
+ visible=False,
612
+ rtl=True
613
+ )
614
+
615
+ with gr.Row():
616
+ with gr.Column():
617
+ gr.HTML('<h2>⚙️ وضعیت سیستم</h2>')
618
+ system_status_btn = gr.Button("📊 نمایش وضعیت سیستم اصلاح شده")
619
+
620
+ system_status_output = gr.Textbox(
621
+ lines=20,
622
+ label="وضعیت سیستم",
623
+ interactive=False,
624
+ visible=False,
625
+ rtl=True
626
+ )
627
+
628
+ # Event handlers
629
+ process_btn.click(
630
+ fn=process_all_steps,
631
+ inputs=[input_text, language_selector, pattern_categories],
632
+ outputs=[status, anonymized_output, gpt_output, final_output]
633
+ )
634
+
635
+ clear_btn.click(
636
+ fn=clear_all,
637
+ outputs=[input_text, anonymized_output, gpt_output, final_output, status]
638
+ )
639
+
640
+ mapping_btn.click(
641
+ fn=get_mapping_table,
642
+ inputs=[language_selector],
643
+ outputs=[mapping_output]
644
+ )
645
+
646
+ mapping_btn.click(
647
+ fn=lambda: gr.update(visible=True),
648
+ outputs=[mapping_output]
649
+ )
650
+
651
+ system_status_btn.click(
652
+ fn=lambda: anonymizer.get_model_status(),
653
+ outputs=[system_status_output]
654
+ )
655
+
656
+ system_status_btn.click(
657
+ fn=lambda: gr.update(visible=True),
658
+ outputs=[system_status_output]
659
+ )
660
+
661
+ if __name__ == "__main__":
662
+ logger.info("🔧 Starting Comprehensive Anonymization System...")
663
+ logger.info("✅ Fixed issues: complex companies, full addresses with squares and towers, invoice numbers, landline phones, names with titles")
664
+ logger.info("🎯 Ready for accurate comprehensive entity detection!")
665
+
666
+ app.launch(
667
+ share=False,
668
+ server_name="0.0.0.0",
669
+ server_port=7860,
670
+ show_error=True
671
+ )