leilaghomashchi commited on
Commit
d820c3b
·
verified ·
1 Parent(s): 4e7d04d

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -1719
app.py DELETED
@@ -1,1719 +0,0 @@
1
- import gradio as gr
2
- import re
3
- import os
4
- import requests
5
- import time
6
- import logging
7
- from packaging import version
8
-
9
- # تنظیم logging
10
- logging.basicConfig(level=logging.INFO)
11
- logger = logging.getLogger(__name__)
12
-
13
- def auto_setup_models():
14
- """راه‌اندازی خودکار مدل‌ها در صورت عدم وجود"""
15
- models_dir = "./models"
16
- required_models = {
17
- 'bert-fa-ner': 'HooshvareLab/bert-fa-zwnj-base-ner',
18
- 'bert-base-NER': 'dslim/bert-base-NER',
19
- }
20
-
21
- missing_models = []
22
- for model_name in required_models.keys():
23
- model_path = os.path.join(models_dir, model_name)
24
- if not os.path.exists(model_path) or not os.listdir(model_path):
25
- missing_models.append(model_name)
26
-
27
- if not missing_models:
28
- logger.info("✅ All models are already available")
29
- return True
30
-
31
- logger.info(f"📥 Auto-downloading missing models: {missing_models}")
32
-
33
- try:
34
- from transformers import AutoTokenizer, AutoModelForTokenClassification
35
- os.makedirs(models_dir, exist_ok=True)
36
-
37
- for model_name in missing_models:
38
- hf_repo = required_models[model_name]
39
- model_path = os.path.join(models_dir, model_name)
40
- logger.info(f"📥 Downloading {model_name} from {hf_repo}...")
41
- try:
42
- tokenizer = AutoTokenizer.from_pretrained(hf_repo)
43
- model = AutoModelForTokenClassification.from_pretrained(hf_repo)
44
- tokenizer.save_pretrained(model_path)
45
- model.save_pretrained(model_path)
46
- logger.info(f"✅ {model_name} downloaded successfully")
47
- del tokenizer, model
48
- except Exception as e:
49
- logger.error(f"❌ Failed to download {model_name}: {e}")
50
- if os.path.exists(model_path):
51
- import shutil
52
- shutil.rmtree(model_path)
53
-
54
- logger.info("🎉 Auto-setup completed!")
55
- return True
56
-
57
- except ImportError:
58
- logger.error("❌ transformers library not available for auto-download")
59
- return False
60
- except Exception as e:
61
- logger.error(f"❌ Auto-setup failed: {e}")
62
- return False
63
-
64
- # اجرای auto-setup در startup
65
- try:
66
- auto_setup_models()
67
- except Exception as e:
68
- logger.warning(f"⚠️ Auto-setup encountered an issue: {e}")
69
- logger.info("ℹ️ Continuing with manual setup...")
70
-
71
- class ComprehensiveBilingualDataAnonymizer:
72
- def __init__(self):
73
- self.mapping_table = {}
74
- # counters به‌روزرسانی شده با تمام دسته‌های جامع (27 دسته)
75
- self.counters = {
76
- # اطلاعات شخصی و هویتی
77
- 'PERSON': 0, 'MIXED_NAMES': 0, 'ID_NUMBER': 0, 'ENGLISH_TITLES': 0,
78
-
79
- # اطلاعات مالی
80
- 'AMOUNT': 0, 'INTERNATIONAL_CURRENCIES': 0, 'ACCOUNT': 0,
81
- 'FINANCIAL_TERMS': 0, 'STOCK_SYMBOL': 0,
82
-
83
- # اطلاعات زمانی
84
- 'DATE': 0, 'ADVANCED_DATE_FORMATS': 0, 'TIME_RANGES': 0,
85
-
86
- # اطلاعات مکانی
87
- 'LOCATION': 0, 'COMPLEX_ADDRESSES': 0,
88
-
89
- # اطلاعات فنی و تکنولوژیکی
90
- 'TECHNICAL_CODES': 0, 'NETWORK_ADDRESSES': 0, 'TECHNICAL_UNITS': 0,
91
- 'ACRONYMS_ABBREVIATIONS': 0,
92
-
93
- # اطلاعات کسب‌وکار
94
- 'COMPANY': 0, 'BUSINESS_TERMS': 0, 'PRODUCT': 0, 'PETROCHEMICAL': 0,
95
-
96
- # اطلاعات کمیت و واحد
97
- 'PERCENTAGE': 0, 'VOLUME': 0, 'RATIOS': 0,
98
-
99
- # اطلاعات ارتباطی
100
- 'PHONE': 0, 'EMAIL': 0
101
- }
102
-
103
- self.api_key = os.getenv("OPENAI_API_KEY", "")
104
- self.models_base_path = "./models"
105
- self.models_loaded = False
106
- self.model_status = {}
107
- self.load_local_ner_models()
108
-
109
- def ensure_models_directory(self):
110
- if not os.path.exists(self.models_base_path):
111
- try:
112
- os.makedirs(self.models_base_path, exist_ok=True)
113
- logger.info(f"📁 Created models directory: {self.models_base_path}")
114
- except Exception as e:
115
- logger.error(f"❌ Failed to create models directory: {e}")
116
- return False
117
- return True
118
-
119
- def download_model_if_missing(self, local_name, hf_repo):
120
- model_path = os.path.join(self.models_base_path, local_name)
121
- if os.path.exists(model_path) and os.listdir(model_path):
122
- return True, f"Model {local_name} already exists"
123
- try:
124
- logger.info(f"📥 Auto-downloading {local_name} from {hf_repo}...")
125
- from transformers import AutoTokenizer, AutoModelForTokenClassification
126
- tokenizer = AutoTokenizer.from_pretrained(hf_repo)
127
- model = AutoModelForTokenClassification.from_pretrained(hf_repo)
128
- tokenizer.save_pretrained(model_path)
129
- model.save_pretrained(model_path)
130
- logger.info(f"✅ {local_name} auto-downloaded successfully")
131
- return True, f"Downloaded {local_name}"
132
- except Exception as e:
133
- logger.error(f"❌ Auto-download failed for {local_name}: {e}")
134
- return False, str(e)
135
-
136
- def _load_pipeline(self, task, model_path, tokenizer_path=None):
137
- """لود مدل با مدیریت صحیح پارامترهای ورژن مختلف transformers"""
138
- try:
139
- from transformers import pipeline, AutoTokenizer, AutoModelForTokenClassification, __version__ as tr_version
140
-
141
- # بررسی پشتیبانی از aggregation_strategy
142
- supports_agg = version.parse(tr_version) >= version.parse("4.11.0")
143
-
144
- # لود توکنایزر و مدل به صورت جداگانه
145
- if tokenizer_path:
146
- tokenizer = AutoTokenizer.from_pretrained(tokenizer_path, local_files_only=True)
147
- else:
148
- tokenizer = AutoTokenizer.from_pretrained(model_path, local_files_only=True)
149
-
150
- model = AutoModelForTokenClassification.from_pretrained(model_path, local_files_only=True)
151
-
152
- # ایجاد pipeline با پارامترهای مناسب
153
- pipeline_kwargs = {
154
- "model": model,
155
- "tokenizer": tokenizer,
156
- "device": -1 # استفاده از CPU
157
- }
158
-
159
- # اضافه کردن aggregation_strategy اگر پشتیبانی می‌شود
160
- if supports_agg:
161
- pipeline_kwargs["aggregation_strategy"] = "simple"
162
-
163
- return pipeline(task, **pipeline_kwargs)
164
-
165
- except Exception as e:
166
- logger.error(f"❌ Failed to load pipeline for {model_path}: {e}")
167
- return None
168
-
169
- def load_local_ner_models(self):
170
- logger.info("📄 Loading local NER models with auto-download...")
171
- if not self.ensure_models_directory():
172
- self.models_loaded = False
173
- self.model_status['directory'] = "❌ Cannot create models directory"
174
- return
175
-
176
- try:
177
- try:
178
- import torch
179
- from transformers import AutoTokenizer, AutoModelForTokenClassification
180
- transformers_available = True
181
- logger.info("✅ Transformers library available")
182
- except ImportError as e:
183
- transformers_available = False
184
- self.model_status['transformers'] = f"❌ Transformers library not installed: {str(e)}"
185
- self.models_loaded = False
186
- return
187
-
188
- # Persian model
189
- persian_model_path = os.path.join(self.models_base_path, "bert-fa-ner")
190
- self.download_model_if_missing("bert-fa-ner", "HooshvareLab/bert-fa-zwnj-base-ner")
191
- if os.path.exists(persian_model_path) and os.listdir(persian_model_path):
192
- try:
193
- self.persian_ner = self._load_pipeline("ner", persian_model_path)
194
- if self.persian_ner:
195
- self.model_status['persian'] = f"✅ Local Persian NER: {persian_model_path}"
196
- else:
197
- self.model_status['persian'] = f"❌ Failed to load Persian model: {persian_model_path}"
198
- except Exception as e:
199
- self.persian_ner = None
200
- self.model_status['persian'] = f"❌ Persian model loading error: {str(e)[:100]}"
201
- else:
202
- self.persian_ner = None
203
- self.model_status['persian'] = f"❌ Persian model not found: {persian_model_path}"
204
-
205
- # English model
206
- english_model_path = os.path.join(self.models_base_path, "bert-base-NER")
207
- self.download_model_if_missing("bert-base-NER", "dslim/bert-base-NER")
208
- if os.path.exists(english_model_path) and os.listdir(english_model_path):
209
- try:
210
- self.english_ner = self._load_pipeline("ner", english_model_path)
211
- if self.english_ner:
212
- self.model_status['english'] = f"✅ Local English NER: {english_model_path}"
213
- else:
214
- self.model_status['english'] = f"❌ Failed to load English model: {english_model_path}"
215
- except Exception as e:
216
- self.english_ner = None
217
- self.model_status['english'] = f"❌ English model loading error: {str(e)[:100]}"
218
- else:
219
- self.english_ner = None
220
- self.model_status['english'] = f"❌ English model not found: {english_model_path}"
221
-
222
- loaded_models = sum(1 for status in self.model_status.values() if status.startswith("✅"))
223
- self.models_loaded = loaded_models > 0
224
- if loaded_models == 0:
225
- self.model_status['fallback'] = "⚠️ Using regex-only mode (no local models found)"
226
-
227
- except Exception as e:
228
- self.models_loaded = False
229
- self.model_status['critical'] = f"❌ Critical error: {str(e)[:100]}..."
230
-
231
- def detect_language(self, text):
232
- """تشخیص زبان متن"""
233
- if not text:
234
- return 'fa'
235
-
236
- persian_chars = len(re.findall(r'[\u0600-\u06FF]', text))
237
- english_chars = len(re.findall(r'[a-zA-Z]', text))
238
- total = persian_chars + english_chars
239
-
240
- if total == 0:
241
- return 'fa'
242
-
243
- if persian_chars / total > 0.6:
244
- return 'fa'
245
- elif english_chars / total > 0.6:
246
- return 'en'
247
- else:
248
- return 'mixed'
249
-
250
- def extract_entities_with_ner(self, text, lang='fa'):
251
- """استخراج entities با مدل‌های NER محلی"""
252
- entities = []
253
-
254
- if not self.models_loaded:
255
- logger.info("ℹ️ Local NER models not available - using regex only")
256
- return entities
257
-
258
- try:
259
- # مدل فارسی محلی
260
- if lang in ['fa', 'mixed'] and hasattr(self, 'persian_ner') and self.persian_ner:
261
- try:
262
- persian_results = self.persian_ner(text)
263
- for entity in persian_results:
264
- # بررسی فرمت خروجی بر اساس ورژن transformers
265
- if isinstance(entity, dict):
266
- if 'entity_group' in entity:
267
- # ورژن جدید با aggregation_strategy
268
- entities.append({
269
- 'text': entity['word'].strip(),
270
- 'label': entity['entity_group'],
271
- 'start': entity['start'],
272
- 'end': entity['end'],
273
- 'confidence': entity['score'],
274
- 'source': 'local_persian_ner'
275
- })
276
- else:
277
- # ورژن قدیمی
278
- entities.append({
279
- 'text': entity['word'].strip(),
280
- 'label': entity['entity'],
281
- 'start': entity['start'],
282
- 'end': entity['end'],
283
- 'confidence': entity['score'],
284
- 'source': 'local_persian_ner'
285
- })
286
- logger.info(f"Local Persian NER found {len(persian_results)} entities")
287
- except Exception as e:
288
- logger.error(f"Local Persian NER extraction error: {e}")
289
-
290
- # مدل انگلیسی محلی
291
- if lang in ['en', 'mixed'] and hasattr(self, 'english_ner') and self.english_ner:
292
- try:
293
- english_results = self.english_ner(text)
294
- for entity in english_results:
295
- # بررسی فرمت خروجی بر اساس ورژن transformers
296
- if isinstance(entity, dict):
297
- if 'entity_group' in entity:
298
- # ورژن جدید با aggregation_strategy
299
- entities.append({
300
- 'text': entity['word'].strip(),
301
- 'label': entity['entity_group'],
302
- 'start': entity['start'],
303
- 'end': entity['end'],
304
- 'confidence': entity['score'],
305
- 'source': 'local_english_ner'
306
- })
307
- else:
308
- # ورژن قدیمی
309
- entities.append({
310
- 'text': entity['word'].strip(),
311
- 'label': entity['entity'],
312
- 'start': entity['start'],
313
- 'end': entity['end'],
314
- 'confidence': entity['score'],
315
- 'source': 'local_english_ner'
316
- })
317
- logger.info(f"Local English NER found {len(english_results)} entities")
318
- except Exception as e:
319
- logger.error(f"Local English NER extraction error: {e}")
320
-
321
- except Exception as e:
322
- logger.error(f"Local NER extraction general error: {e}")
323
-
324
- # حذف تکراری‌ها
325
- unique_entities = []
326
- seen = set()
327
- for entity in entities:
328
- key = (entity['text'].lower(), entity['start'], entity['end'])
329
- if key not in seen:
330
- seen.add(key)
331
- unique_entities.append(entity)
332
-
333
- logger.info(f"Total unique entities found by local models: {len(unique_entities)}")
334
- return unique_entities
335
-
336
- def map_ner_to_categories(self, ner_label, source=''):
337
- """نگاشت برچسب‌های NER به دسته‌های سیستم"""
338
- mapping = {
339
- 'PER': 'PERSON', 'PERSON': 'PERSON',
340
- 'ORG': 'COMPANY', 'ORGANIZATION': 'COMPANY',
341
- 'LOC': 'LOCATION', 'LOCATION': 'LOCATION',
342
- 'MISC': 'BUSINESS_TERMS', 'MISCELLANEOUS': 'BUSINESS_TERMS',
343
- 'B-PER': 'PERSON', 'I-PER': 'PERSON',
344
- 'B-ORG': 'COMPANY', 'I-ORG': 'COMPANY',
345
- 'B-LOC': 'LOCATION', 'I-LOC': 'LOCATION',
346
- 'B-MISC': 'BUSINESS_TERMS', 'I-MISC': 'BUSINESS_TERMS',
347
- 'MONEY': 'AMOUNT', 'PERCENT': 'PERCENTAGE',
348
- 'DATE': 'DATE', 'TIME': 'DATE'
349
- }
350
- return mapping.get(ner_label.upper(), 'BUSINESS_TERMS')
351
-
352
- def get_comprehensive_patterns(self):
353
- """الگوهای جامع ناشناس‌سازی بر اساس 221 الگوی دسته‌بندی شده"""
354
- return {
355
- # =============================================================================
356
- # 1. اطلاعات شخصی و هویتی (PERSONAL & IDENTITY INFORMATION) - 30 الگو
357
- # =============================================================================
358
-
359
- 'PERSON': [
360
- # نام‌ها با عناوین فارسی
361
- r'آقای\s+([آ-یa-zA-Z]+(?:\s+[آ-یa-zA-Z]+)*)',
362
- r'خانم\s+([آ-یa-zA-Z]+(?:\s+[آ-یa-zA-Z]+)*)',
363
- r'مهندس\s+([آ-یa-zA-Z]+(?:\s+[آ-یa-zA-Z]+)*)',
364
- r'دکتر\s+([آ-یa-zA-Z]+(?:\s+[آ-یa-zA-Z]+)*)',
365
- r'استاد\s+([آ-یa-zA-Z]+(?:\s+[آ-یa-zA-Z]+)*)',
366
- # نام‌ها با سمت
367
- r'([آ-یa-zA-Z]+\s+[آ-یa-zA-Z]+)(?:،\s+مدیرعامل|\s+مدیرعامل|\s+رئیس)',
368
- r'مدیرعامل(?=\s|$|،|\.)',
369
- r'سرپرست(?=\s+و|\s|$|،|\.)',
370
- r'رئیس\s+هیأت‌مدیره',
371
- # ضمایر اشاره‌ای
372
- r'وی(?=\s+ادامه|\s+اظهار|\s+گفت|\s+اعلام|\s+همچنین)',
373
- # عناوین انگلیسی
374
- r'Mr\.\s+([a-zA-Z]+(?:\s+[a-zA-Z]+)*)',
375
- r'Ms\.\s+([a-zA-Z]+(?:\s+[a-zA-Z]+)*)',
376
- r'Dr\.\s+([a-zA-Z]+(?:\s+[a-zA-Z]+)*)',
377
- # نام‌های کامل
378
- r'([آ-یa-zA-Z]{3,}\s+[آ-یa-zA-Z]{3,})(?=\s+گفت|\s+اظهار|\s+اعلام)'
379
- ],
380
-
381
- 'MIXED_NAMES': [
382
- # نام‌های فارسی-انگلیسی
383
- r'([آ-ی]+[a-zA-Z\s]+[آ-ی]+)',
384
- r'Dr\.\s+([آ-یa-zA-Z\s‌]+)',
385
- # نام‌های کامل بدون عنوان
386
- r'([آ-یa-zA-Z]{2,}\s+[آ-یa-zA-Z]{2,})',
387
- # نام‌های انگلیسی با خط تیره
388
- r'([A-Z][a-z]+-[A-Z][a-z]+)',
389
- r"([A-Z]'[A-Z][a-z]+)",
390
- # نام‌های رومن
391
- r'([A-Z][a-z]+\s+[A-Z][a-z]+\s+[IVX]+)',
392
- # نام‌های ترکیبی با سمت
393
- r'([a-z\s]+)\s+([آ-ی\s]+)',
394
- # نام‌های تجاری
395
- r'([A-Z][a-z]+\s+[A-Z][a-z]+)\s*\(([A-Z][a-z]+\s+[A-Z][a-z]+)\)'
396
- ],
397
-
398
- 'ID_NUMBER': [
399
- # شماره شبا ایرانی
400
- r'IR[۰-۹0-9]{24}',
401
- r'شبا[\s:]*IR[۰-۹0-9]{24}',
402
- r'IBAN[\s:]*IR[۰-۹0-9]{24}',
403
- r'شماره[\s]*شبا[\s:]*IR[۰-۹0-9]{24}',
404
- # کد ملی
405
- r'(?:کد[\s]*)?(?:ملی[\s:]*)?[۰-۹0-9]{10}',
406
- r'(?:شناسه[\s]*)?(?:ملی[\s:]*)?[۰-۹0-9]{10}',
407
- r'National[\s]*(?:ID[\s:]*)?[0-9]{10}',
408
- # پاسپورت
409
- r'(?:پاسپورت[\s:]*)?[A-Z][0-9]{8}',
410
- r'(?:Passport[\s:]*)?[A-Z][0-9]{8}',
411
- # کارت‌های بانکی
412
- r'(?:کارت[\s:]*)?(?:[۰-۹0-9]{4}[-\s]?){3}[۰-۹0-9]{4}',
413
- r'(?:Card[\s:]*)?(?:[0-9]{4}[-\s]?){3}[0-9]{4}',
414
- # شماره‌های SSN و FICO
415
- r'SSN[\s:]*[0-9]{3}-[0-9]{2}-[0-9]{4}',
416
- r'FICO[\s]*(?:score[\s:]*)?[0-9]{3}',
417
- # شماره‌های اداری
418
- r'EIN[\s:]*[0-9]{2}-[0-9]{7}',
419
- r'Meeting[\s]*ID[\s:]*[0-9]{9,11}'
420
- ],
421
-
422
- 'ENGLISH_TITLES': [
423
- # عناوین تجاری
424
- r'business\s+partner',
425
- r'team\s+lead',
426
- r'head\s+of\s+production',
427
- # عناوین مهندسی
428
- r'senior\s+architect',
429
- r'civil\s+engineer',
430
- r'quantity\s+surveyor',
431
- r'system\s+administrator',
432
- r'network\s+engineer',
433
- # عناوین مشاوره‌ای
434
- r'environmental\s+consultant',
435
- r'HSE\s+coordinator',
436
- # عناوین مالی
437
- r'senior\s+loan\s+officer',
438
- r'investment\s+advisor',
439
- r'Chief\s+Financial\s+Officer',
440
- # عناوین مدیریتی
441
- r'facility\s+manager',
442
- r'quality\s+control\s+manager',
443
- r'maintenance\s+window',
444
- r'project\s+team',
445
- r'technical\s+support',
446
- # فرآیندهای کاری
447
- r'supervision',
448
- r'troubleshooting',
449
- r'monitoring',
450
- r'compliance\s+certificate'
451
- ],
452
-
453
- # =============================================================================
454
- # 2. اطلاعات مالی (FINANCIAL INFORMATION) - 37 الگو
455
- # =============================================================================
456
-
457
- 'AMOUNT': [
458
- # مبالغ فارسی
459
- r'\d+(?:,\d{3})*\s*(?:میلیون|میلیارد|هزار)\s*تومان',
460
- r'مبلغ\s+\d+(?:,\d{3})*\s*(?:میلیون|میلیارد|هزار)?\s*تومان',
461
- r'\d+\s*تومان(?=\s+به\s+ازای|\s+فروش|،)',
462
- r'رقم\s+فعلی\s+\d+(?:,\d{3})*\s*(?:میلیون|میلیارد)\s*تومان',
463
- r'رقم\s+\d+(?:,\d{3})*\s*(?:میلیون|میلیارد)\s*تومان',
464
- r'به\s+\d+(?:,\d{3})*\s*(?:میلیون|میلیارد|هزار)\s*تومان',
465
- r'از\s+\d+(?:,\d{3})*\s*(?:میلیون|میلیارد|هزار)\s*تومان',
466
- r'برابر\s+با\s+\d+(?:,\d{3})*\s*(?:میلیون|میلیارد|هزار)\s*تومان',
467
- r'\d+(?:میلیارد|میلیون)\s*تومان(?=\s+رسیده|\s+ثبت|\s+بوده|،)',
468
- # مبالغ دلار
469
- r'\$\d+(?:,\d{3})*(?:\.\d+)?\s*(?:million|billion|thousand|M|B|K)?',
470
- r'\d+(?:,\d{3})*\s*ریال',
471
- # یورو
472
- r'€\d+(?:,\d{3})*(?:\.\d+)?',
473
- # درهم
474
- r'\d+(?:,\d{3})*\s*AED',
475
- # فرمت‌های K/M
476
- r'\$\d+(?:\.\d+)?[KMB]',
477
- r'€\d+(?:\.\d+)?[KM]'
478
- ],
479
-
480
- 'INTERNATIONAL_CURRENCIES': [
481
- # یورو با فرمت‌های مختلف
482
- r'\d+(?:,\d{3})*\s+euro',
483
- r'€\d+(?:\.\d+)?M',
484
- r'\d+\s+EUR',
485
- # درهم امارات
486
- r'\d+(?:,\d{3})*\s+AED',
487
- r'\d+(?:\.\d+)?M\s+AED',
488
- # دلار با فرمت K/M
489
- r'\$\d+(?:\.\d+)?M',
490
- r'\$\d+(?:\.\d+)?K',
491
- # پوند انگلیس
492
- r'£\d+(?:,\d{3})*(?:\.\d+)?',
493
- r'\d+\s+GBP',
494
- # فرانک سوئیس
495
- r'\d+\s+CHF',
496
- # ین ژاپن
497
- r'¥\d+(?:,\d{3})*',
498
- r'\d+\s+JPY'
499
- ],
500
-
501
- 'ACCOUNT': [
502
- # حساب‌های بانکی فارسی
503
- r'(?:شماره[\s]*)?(?:حساب[\s]*)?(?:بانکی[\s:]*)?(?:[۰-۹0-9]{1,3}[-\s]?)*[۰-۹0-9]{8,20}',
504
- r'حساب[\s]*(?:شماره[\s:]*)?(?:[۰-۹0-9]{1,3}[-\s]?)*[۰-۹0-9]{8,20}',
505
- r'شماره[\s]*حساب[\s:]*(?:[۰-۹0-9]{1,3}[-\s]?)*[۰-۹0-9]{8,20}',
506
- # حساب‌های انگلیسی
507
- r'Account[\s]*(?:Number[\s:]*)?(?:[0-9]{1,3}[-\s]?)*[0-9]{8,20}',
508
- r'[۰-۹0-9]{3}[-\s]?[۰-۹0-9]{3}[-\s]?[۰-۹0-9]{6,12}',
509
- r'[۰-۹0-9]{2,4}[-\s]?[۰-۹0-9]{6,12}[-\s]?[۰-۹0-9]{2,4}',
510
- # واریز و سود
511
- r'واریز[\s]*(?:سود[\s:]*)?(?:[۰-۹0-9]{1,3}[-\s]?)*[۰-۹0-9]{8,20}',
512
- r'سود[\s:]*(?:[۰-۹0-9]{1,3}[-\s]?)*[۰-۹0-9]{8,20}'
513
- ],
514
-
515
- 'FINANCIAL_TERMS': [
516
- # اصطلاحات فروش
517
- r'فروش\s+(?:ماهانه|تجمیعی|صادراتی)',
518
- r'درآمد\s+شرکت',
519
- r'سود\s+(?:خالص|نقدی)',
520
- r'صورت‌های\s+مالی',
521
- r'بهای\s+تمام‌شده',
522
- r'سودآوری',
523
- r'عملکرد\s+مالی',
524
- r'میانگین\s+فروش',
525
- r'بالاترین\s+رقم\s+فروش',
526
- r'رقم\s+فروش',
527
- r'د��آمدهای\s+عملیاتی'
528
- ],
529
-
530
- 'STOCK_SYMBOL': [
531
- # نمادهای بورس ایران
532
- r'نماد\s+([آ-یa-zA-Z0-9]+)',
533
- r'(سبهان|غدیر|شتران|شپنا|پترول|فارس|خارک|پلاسکو|جم|کرمان|مارون|اراک|رازی|شازند|کاوه|بندر|پارس|خوزستان|ماهشهر|عسلویه)(?=\s|$|،|\.|\s+)',
534
- r'شرکت\s+([آ-یa-zA-Z\s]+?)(?=\s+در|\s+که|\s+با|،|\.|\s+$|\s+را|\s+به)',
535
- r'پتروشیمی\s+([آ-یa-zA-Z\s]+?)(?=\s+در|\s+که|\s+با|،|\.|\s+$|\s+توان)',
536
- # نمادهای بین‌المللی
537
- r'(AAPL|GOOGL|MSFT|AMZN|TSLA|META|NVDA|SABIC)(?=\s|$|,|\.)'
538
- ],
539
-
540
- # =============================================================================
541
- # 3. اطلاعات زمانی (TEMPORAL INFORMATION) - 30 الگو
542
- # =============================================================================
543
-
544
- 'DATE': [
545
- # تاریخ‌های فارسی
546
- r'[۰-۹0-9]{4}[/-][۰-۹0-9]{1,2}[/-][۰-۹0-9]{1,2}',
547
- r'[۰-۹0-9]{1,2}[/-][۰-۹0-9]{1,2}[/-][۰-۹0-9]{4}',
548
- r'(?:[۰-۹0-9]{1,2})\s*(?:فروردین|اردیبهشت|خرداد|تیر|مرداد|شهریور|مهر|آبان|آذر|دی|بهمن|اسفند)\s*(?:[۰-۹0-9]{4})',
549
- # ماه‌های فارسی
550
- r'(?:فروردین|اردیبهشت|خرداد|تیر|مرداد|شهریور|مهر|آبان|آذر|دی|بهمن|اسفند)\s+[۰-۹0-9]{4}',
551
- # تاریخ‌های انگلیسی
552
- r'(?:[0-9]{1,2})\s*(?:January|February|March|April|May|June|July|August|September|October|November|December)\s*(?:[0-9]{4})',
553
- r'(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s*[0-9]{1,2},?\s*[0-9]{4}',
554
- # بازه‌های زمانی
555
- r'سال\s+گذشته',
556
- r'سال\s+جاری',
557
- r'این\s+سال',
558
- r'ماه\s+قبل',
559
- r'ماه\s+اخیر',
560
- r'دومین\s+ماه\s+سال',
561
- r'ابتدای\s+سال\s+جاری',
562
- r'مدت\s+مشابه\s+سال\s+گذشته',
563
- r'چند\s+ماهه\s+اخیر',
564
- # سال‌های مستقل
565
- r'(?:13[0-9]{2}|14[0-9]{2}|20[0-9]{2}|19[0-9]{2})(?=\s|$|،|\.)'
566
- ],
567
-
568
- 'ADVANCED_DATE_FORMATS': [
569
- # تاریخ انگلیسی
570
- r'(?:March|April|May|June|July|August|September|October|November|December)\s+\d{1,2}(?:st|nd|rd|th),?\s+\d{4}',
571
- r'(?:January|February)\s+\d{1,2}(?:st|nd|rd|th),?\s+\d{4}',
572
- # timestamp
573
- r'\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z',
574
- # timezone
575
- r'(?:PST|EST|GMT|UTC)(?:[+-]\d{1,2}:\d{2})?',
576
- r'Eastern\s+Time',
577
- r'GMT[+-]\d{1,2}:\d{2}',
578
- # تاریخ با ساعت
579
- r'\d{1,2}(?:st|nd|rd|th)\s+of\s+(?:January|February|March|April|May|June|July|August|September|October|November|December)\s+\d{4}',
580
- # بازه تاریخ
581
- r'ending\s+(?:December|January|February|March|April|May|June|July|August|September|October|November)\s+\d{1,2}(?:st|nd|rd|th)',
582
- # fiscal year
583
- r'end\s+of\s+fiscal\s+year\s+\d{4}/\d{2}/\d{2}',
584
- # due date
585
- r'\d{1,2}\s+(?:روز|days?)\s+(?:کاری|business)\s+پس\s+از\s+(?:delivery|تحویل)',
586
- # فرمت COB
587
- r'COB\s+(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)'
588
- ],
589
-
590
- 'TIME_RANGES': [
591
- # shift time
592
- r'\d{2}:\d{2}-\d{2}:\d{2}',
593
- r'\d{2}:\d{2}\s+تا\s+\d{2}:\d{2}',
594
- # maintenance window
595
- r'(?:Saturday|Sunday|Monday|Tuesday|Wednesday|Thursday|Friday)\s+night\s+\d{1,2}:\d{2}\s+(?:AM|PM)\s+to\s+\d{1,2}:\d{2}\s+(?:AM|PM)',
596
- # meeting time
597
- r'\d{1,2}:\d{2}\s+(?:AM|PM)\s+(?:PST|EST|GMT|UTC)',
598
- r'\d{1,2}:\d{2}\s+(?:AM|PM)\s+Eastern\s+Time',
599
- # timestamp
600
- r'\d{2}:\d{2}:\d{2}\s+(?:AM|PM)',
601
- # business hours
602
- r'COB\s*\(Close\s+of\s+Business\)',
603
- # due periods
604
- r'\d{1,3}\s+(?:business\s+days|روز\s+کاری)',
605
- r'warranty\s+period\s+(?:دو\s+سال|\d+\s+(?:years?|سال))'
606
- ],
607
-
608
- # =============================================================================
609
- # 4. اطلاعات مکانی (LOCATION INFORMATION) - 14 الگو
610
- # =============================================================================
611
-
612
- 'LOCATION': [
613
- # شهرهای ایران
614
- r'(تهران|اصفهان|ماهشهر|عسلویه|بندرعباس|اهواز|شیراز|مشهد|تبریز|کرج|قم|رشت|کرمان|یزد|زاهدان|بوشهر|خرمشهر|آبادان|اراک|قزوین)',
615
- # استان‌ها
616
- r'استان\s+([آ-ی\s]+)',
617
- r'شهر\s+([آ-ی\s]+)',
618
- # کشورها
619
- r'(ایران|عراق|کویت|عربستان|امارات|قطر|عمان|بحرین|ترکیه|پاکستان|افغانستان|آذربایجان|ارمنستان|گرجستان)',
620
- # داخلی/خارجی
621
- r'داخلی|بازار\s+داخلی',
622
- r'خارجی|بازارهای\s+خارجی',
623
- # شهرهای بین‌المللی
624
- r'(London|Paris|Tokyo|New\s+York|Dubai|Singapore|Hong\s+Kong|Shanghai|Mumbai|Frankfurt|Amsterdam)'
625
- ],
626
-
627
- 'COMPLEX_ADDRESSES': [
628
- # آدرس با کیلومتر
629
- r'کیلومتر\s+\d+\s+جاده\s+[آ-ی\s]+-[آ-ی\s]+',
630
- # آدرس با مرجع
631
- r'روبروی\s+(?:پمپ\s+بنزین|بانک|پارک|مسجد|بیمارستان)\s+[آ-یa-zA-Z\s]+',
632
- # آدرس ساختمان
633
- r'Building-[A-Z],?\s+Floor-\d+,?\s+Unit-[A-Z0-9]+',
634
- # آدرس rack
635
- r'rack\s+number\s+R-\d+,?\s+slot\s+\d+',
636
- # آدرس plot
637
- r'phase\s+\d+\s+development,?\s+block\s+[A-Z],?\s+plot\s+\d+-[A-Z]',
638
- # آدرس آمریکایی
639
- r'\d{2,5}\s+[A-Z][a-z]+\s+(?:Street|Avenue|Boulevard|Road|Drive),?\s+Floor\s+\d+,?\s+Building\s+[A-Z]',
640
- # industrial estate
641
- r'شهرک\s+صنعتی\s+[آ-ی\s]+،?\s+محور\s+[آ-ی\s]+',
642
- # data center
643
- r'[آ-ی\s]+-پارک\s+فناوری\s+[آ-ی\s]+'
644
- ],
645
-
646
- # =============================================================================
647
- # 5. اطلاعات فنی و تکنولوژیکی (TECHNICAL & TECHNOLOGICAL) - 32 الگو
648
- # =============================================================================
649
-
650
- 'TECHNICAL_CODES': [
651
- # کدهای سریال
652
- r'SN-\d{4}-[A-Z]{3}-\d{4}',
653
- r'Serial\s+Number[\s:]*[A-Z0-9-]+',
654
- # کدهای مرجع
655
- r'REF-[A-Z]{3}-\d{4}-\d{3}',
656
- r'DOC-[A-Z]{2}-\d{4}-\d{4}',
657
- # کدهای پروژه
658
- r'INF-\d{4}-\d{4}',
659
- r'CTR/\d{4}/\d{3}',
660
- # شناسه‌های فنی
661
- r'HVAC-\d{7}',
662
- r'Generator-Model-[A-Z0-9]+',
663
- # کدهای LOI/BOQ
664
- r'LOI-\d{4}-[A-Z]{4}-\d{3}',
665
- r'BOQ-\d{4}-[A-Z]{3}-\d{3}',
666
- # شماره‌های invoice
667
- r'#INV-\d{4}-Q\d-\d{4}',
668
- # کدهای ESC
669
- r'ESC-\d{4}-[A-Z]{3}-\d{3}',
670
- # کدهای batch
671
- r'BN-\d{6}-[A-Z]\d+'
672
- ],
673
-
674
- 'NETWORK_ADDRESSES': [
675
- # آدرس IP
676
- r'\b(?:\d{1,3}\.){3}\d{1,3}\b',
677
- r'xxx\.xxx\.xxx\.xxx',
678
- # آدرس MAC
679
- r'[A-F0-9]{2}:[A-F0-9]{2}:[A-F0-9]{2}:[A-F0-9]{2}:[A-F0-9]{2}:[A-F0-9]{2}',
680
- # hostname
681
- r'srv-[a-z]+-[a-z]+-\d{2}',
682
- r'[a-z]+-[a-z]+\d*\.[a-z]+\.[a-z]+',
683
- # domain names
684
- r'[a-zA-Z0-9-]+\.[a-zA-Z]{2,4}(?:\.[a-zA-Z]{2,4})?'
685
- ],
686
-
687
- 'TECHNICAL_UNITS': [
688
- # واحدهای برق
689
- r'\d+(?:\.\d+)?\s*MW',
690
- r'\d+(?:\.\d+)?\s*kWh?',
691
- # واحدهای حجم
692
- r'\d+(?:,\d{3})*\s*cubic\s+meters',
693
- r'\d+(?:,\d{3})*\s*m³',
694
- r'\d+(?:,\d{3})*\s*sq\s+ft',
695
- # واحدهای آلودگی
696
- r'\d+(?:\.\d+)?\s*ppm',
697
- r'\d+(?:\.\d+)?\s*mg/m³',
698
- r'\b(?:CO2|NOx|SO2)\b',
699
- # واحدهای دیجیتال
700
- r'\d+(?:\.\d+)?\s*TB',
701
- r'\d+(?:\.\d+)?\s*GB',
702
- # واحدهای مساحت
703
- r'\d+(?:,\d{3})*\s*square\s+meters',
704
- r'\d+(?:\.\d+)?\s*per\s+sq\s+ft\s+NNN',
705
- # efficiency rate
706
- r'\d+(?:\.\d+)?\%\s*efficiency',
707
- r'score:\s*\d+(?:\.\d+)?/10',
708
- # FICO score
709
- r'FICO\s+score:\s*\d{3}',
710
- # واحدهای فشار
711
- r'\d+(?:\.\d+)?\s*(?:bar|psi)',
712
- # واحدهای دما
713
- r'\d+(?:\.\d+)?\s*°[CF]',
714
- # واحدهای سرعت
715
- r'\d+(?:\.\d+)?\s*(?:rpm|m/s)'
716
- ],
717
-
718
- 'ACRONYMS_ABBREVIATIONS': [
719
- # فنی
720
- r'\b(?:HVAC|IT|HSE|BOQ|LC|COB)\b',
721
- # مالی
722
- r'\b(?:YTD|NNN|EIN|SSN|FICO)\b',
723
- # تکنولوژی
724
- r'\bIP\s+Address\b',
725
- r'\bMAC\s+Address\b',
726
- r'\bURL\b',
727
- # کسب‌وکار
728
- r'\b(?:LLC|Corp|Inc|Ltd)\b',
729
- # تاریخ
730
- r'\b(?:PST|GMT|UTC|EST)\b',
731
- # علمی
732
- r'\b(?:CO2|NOx|pH|UV)\b',
733
- # مهندسی
734
- r'\b(?:SCADA|PLC|HMI)\b',
735
- # اقتصادی
736
- r'\b(?:GDP|CPI|ROI|NPV)\b',
737
- # حمل‌ونقل
738
- r'\b(?:FOB|CIF|DDP)\b',
739
- # بانکی
740
- r'\b(?:ABA|SWIFT|IBAN)\b'
741
- ],
742
-
743
- # =============================================================================
744
- # 6. اطلاعات کسب‌وکار (BUSINESS INFORMATION) - 39 الگو
745
- # =============================================================================
746
-
747
- 'COMPANY': [
748
- # شرکت‌های فارسی
749
- r'شرکت(?=\s+در|\s+که|\s+با|\s+را|\s+به|\s+طی)',
750
- r'([آ-یa-zA-Z\s]+)\s+شرکت',
751
- r'این\s+شرکت(?=\s|$|،|\.)',
752
- # بانک‌ها
753
- r'(بانک\s+[آ-یa-zA-Z\s]+)',
754
- # شرکت‌های بین‌المللی
755
- r'([A-Z][a-zA-Z\s]+(?:Inc|Corp|Corporation|Company|Ltd|Limited|LLC))'
756
- ],
757
-
758
- 'BUSINESS_TERMS': [
759
- # تحلیل و گزارش
760
- r'تحلیل\s+عملکرد',
761
- r'گزارش\s+(?:فعالیت|عملکرد)\s+ماهانه',
762
- r'وضعیت\s+فروش',
763
- # تولید و بازار
764
- r'تولید\s+پایدار',
765
- r'سهم\s+بازار',
766
- r'صادرات\s+هدفمند',
767
- r'بهره‌وری',
768
- r'ظرفیت‌های\s+داخلی',
769
- # صنعت و رقابت
770
- r'شرکت‌های\s+پیشرو',
771
- r'صنعت\s+پتروشیمی',
772
- r'سرمایه‌گذاران\s+بنیادی',
773
- # شاخص‌ها و برنامه‌ریزی
774
- r'شاخص‌های\s+عملیاتی',
775
- r'برنامه‌ریزی\s+مناسب',
776
- # فروش و انبار
777
- r'واحد\s+فروش',
778
- r'موجودی\s+انبار',
779
- # رشد و توسعه
780
- r'فاز\s+رشد\s+جدید',
781
- r'ترکیب\s+فروش',
782
- r'سهم\s+صادراتی',
783
- # عملکرد و داده‌ها
784
- r'روند\s+عملکرد',
785
- r'اعداد\s+اعلام‌شده',
786
- r'داده‌های\s+ثبت‌شده'
787
- ],
788
-
789
- 'PRODUCT': [
790
- # محصولات پتروشیمی
791
- r'\b(?:VCM|PVC|PE|PP|PS|ABS|SAN|PC|PMMA|PET|PBT|PA|POM|TPU)\b',
792
- # پلیمرها
793
- r'پلی\s*(?:اتیلن|پروپیلن|استایرن|کربنات|متیل)',
794
- # مواد شیمیایی
795
- r'\b(?:اتیلن|پروپیلن|بنزن|تولوئن|زایلن|متانول|اتانول|استون|فنول)\b',
796
- # گازها
797
- r'\b(?:کلر|هیدروژن|اکسیژن|نیتروژن|آمونیاک|اتان|پروپان|بوتان)\b',
798
- # محصولات عمومی
799
- r'محصول(?:ات)?',
800
- r'تولیدات\s+شرکت'
801
- ],
802
-
803
- 'PETROCHEMICAL': [
804
- # نام‌های اختصاری پتروشیمی‌ها
805
- r'\b(?:LDPE|HDPE|LLDPE|PP|PS|EPS|ABS|SAN|PC|PMMA|PET|PBT|PA6|PA66|POM|TPU|EVA|EAA)\b',
806
- # ترکیبات شیمیایی پیچیده
807
- r'(?:Ethylene\s+Vinyl\s+Acetate|Ethyl\s+Acrylate|Methyl\s+Methacrylate|Polyethylene\s+Terephthalate)'
808
- ],
809
-
810
- # =============================================================================
811
- # 7. اطلاعات کمیت و واحد (QUANTITY & UNIT INFORMATION) - 26 الگو
812
- # =============================================================================
813
-
814
- 'PERCENTAGE': [
815
- # درصدهای فارسی
816
- r'\d+(?:\.\d+)?\s*درصد(?:\s+افزایش|\s+رشد|\s+کاهش|\s+بالاتر|\s+پایین‌تر)?',
817
- r'\d+(?:\.\d+)?\s*%',
818
- r'معادل\s+\d+(?:\.\d+)?\s*درصد',
819
- r'حدود\s+\d+(?:\.\d+)?\s*درصد',
820
- r'با\s+\d+(?:\.\d+)?\s*درصد\s+افزایش',
821
- r'رشد\s+\d+(?:\.\d+)?\s*درصدی',
822
- r'\d+(?:\.\d+)?\s*درصدی(?=\s+همراه|\s+بوده)',
823
- # عبارات کیفی
824
- r'میزان\s+رشد(?=\s+نسبت|\s+معادل)',
825
- r'افزایش\s+قابل‌توجهی',
826
- r'بهبود\s+نسبی',
827
- # درصدهای انگلیسی
828
- r'\d+(?:\.\d+)?\%\s*(?:increase|decrease|growth|improvement)',
829
- r'(?:approximately|about)\s+\d+(?:\.\d+)?\%'
830
- ],
831
-
832
- 'VOLUME': [
833
- # حجم‌های فارسی
834
- r'\d+(?:,\d{3})*\s*تن',
835
- r'\d+(?:,\d{3})*\s*(?:کیلوگرم|لیتر|بشکه)',
836
- r'میزان\s+\d+(?:,\d{3})*\s*تن',
837
- r'مقدار\s+تولید',
838
- r'حجم\s+فروش',
839
- r'ظرفیت\s+(?:تولید|اسمی)',
840
- # واحدهای انگلیسی
841
- r'\d+(?:,\d{3})*\s*(?:tons|kg|liters|barrels)',
842
- r'\d+(?:,\d{3})*\s*(?:metric\s+tons|MT)',
843
- r'\d+(?:,\d{3})*\s*(?:thousand\s+tons|KT)'
844
- ],
845
-
846
- 'RATIOS': [
847
- # نسبت‌ها
848
- r'نسبت\s+(?:فروش|تولید)\s+به\s+[آ-ی\s]+',
849
- r'\d+(?:\.\d+)?\s*نزدیک',
850
- r'برابر\s+با\s+\d+(?:\.\d+)?',
851
- r'معادل\s+\d+(?:\.\d+)?',
852
- r'میزان\s+(?:رشد|افزایش)',
853
- r'شاخص\s+(?:مهم|عملیاتی)',
854
- r'\d+(?:\.\d+)?\s*درصد\s+کل\s+تولید'
855
- ],
856
-
857
- # =============================================================================
858
- # 8. اطلاعات ارتباطی (COMMUNICATION INFORMATION) - 5 الگو
859
- # =============================================================================
860
-
861
- 'PHONE': [
862
- # شماره‌های فارسی
863
- r'(?:تلفن[\s:]*)?(?:شماره[\s:]*)?(?:0)?(?:[۰-۹0-9]{2,3}[-\s]?)?[۰-۹0-9]{7,8}',
864
- r'(?:تماس[\s:]*)?(?:شماره[\s:]*)?(?:با[\s]*)?(?:0)?(?:[۰-۹0-9]{2,3}[-\s]?)?[۰-۹0-9]{7,8}',
865
- r'(?:موبایل[\s:]*)?(?:شماره[\s:]*)?(?:0)?9[۰-۹0-9]{9}',
866
- r'[۰-۹0-9]{3,4}[-\s][۰-۹0-9]{7,8}',
867
- r'[۰-۹0-9]{11}(?!\d)',
868
- r'(?:\+98|0098)?[۰-۹0-9]{10}',
869
- r'[۰-۹0-9]{3,4}[-\s]?[۰-۹0-9]{3,4}[-\s]?[۰-۹0-9]{3,4}',
870
- # شماره‌های بین‌المللی
871
- r'\+[0-9]{1,3}-[0-9]{3}-[0-9]{3}-[0-9]{4}(?:\s+ext\.\s+[0-9]{3,4})?',
872
- r'\([0-9]{3}\)\s+[0-9]{3}-[0-9]{4}'
873
- ],
874
-
875
- 'EMAIL': [
876
- # ایمیل‌های استاندارد
877
- r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}',
878
- # ایمیل‌های با کلمات کلیدی فارسی
879
- r'ایمیل[\s:]*[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}',
880
- r'email[\s:]*[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}',
881
- r'نشانی[\s]*الکترونیکی[\s:]*[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}',
882
- r'آدرس[\s]*ایمیل[\s:]*[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}',
883
- # ایمیل‌های کاری خاص
884
- r'facility\.manager@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}'
885
- ]
886
- }
887
-
888
- def anonymize_text(self, original_text, lang='fa'):
889
- """گام 1: ناشناس‌سازی متن با الگوهای جامع"""
890
- try:
891
- if not original_text or not original_text.strip():
892
- return "❌ Please enter input text!" if lang == 'en' else "❌ لطفاً متن ورودی را وارد کنید!"
893
-
894
- # ریست متغیرها
895
- self.mapping_table = {}
896
- self.counters = {key: 0 for key in self.counters.keys()}
897
-
898
- anonymized = original_text
899
- found_entities = set()
900
-
901
- # تشخیص زبان
902
- detected_lang = self.detect_language(original_text)
903
- logger.info(f"Detected language: {detected_lang}")
904
-
905
- # مرحله 1: استخراج با Local NER
906
- if self.models_loaded:
907
- logger.info("🤖 Running comprehensive local NER extraction...")
908
- ner_entities = self.extract_entities_with_ner(original_text, detected_lang)
909
-
910
- for entity in ner_entities:
911
- if (entity['text'] not in found_entities and
912
- len(entity['text'].strip()) > 1 and
913
- entity['confidence'] > 0.5):
914
-
915
- category = self.map_ner_to_categories(entity['label'], entity['source'])
916
-
917
- if entity['text'] not in self.mapping_table:
918
- self.counters[category] += 1
919
- code = f"{category}_{self.counters[category]:03d}_LOCAL_NER"
920
- self.mapping_table[entity['text']] = code
921
- found_entities.add(entity['text'])
922
- logger.info(f"Local NER: {entity['text']} -> {code}")
923
- else:
924
- logger.info("ℹ️ Using comprehensive regex-only mode")
925
-
926
- # مرحله 2: الگوهای Regex جامع - 221 الگو
927
- patterns = self.get_comprehensive_patterns()
928
-
929
- # پردازش patterns با اولویت‌بندی - از خاص به عام
930
- logger.info("🔍 Running comprehensive priority-based regex extraction...")
931
-
932
- # پردازش به ترتیب اولویت برای جلوگیری از تداخل
933
- processed_entities = set() # برای جلوگیری از تکرار
934
-
935
- # اولویت‌بندی دسته‌ها بر اساس حساسیت
936
- priority_order = [
937
- 'ID_NUMBER', 'EMAIL', 'PHONE', 'ACCOUNT', 'TECHNICAL_CODES',
938
- 'NETWORK_ADDRESSES', 'INTERNATIONAL_CURRENCIES', 'AMOUNT',
939
- 'TECHNICAL_UNITS', 'ACRONYMS_ABBREVIATIONS', 'ADVANCED_DATE_FORMATS',
940
- 'TIME_RANGES', 'COMPLEX_ADDRESSES', 'MIXED_NAMES', 'ENGLISH_TITLES',
941
- 'STOCK_SYMBOL', 'COMPANY', 'PERSON', 'PERCENTAGE', 'VOLUME',
942
- 'RATIOS', 'LOCATION', 'DATE', 'FINANCIAL_TERMS', 'BUSINESS_TERMS',
943
- 'PRODUCT', 'PETROCHEMICAL'
944
- ]
945
-
946
- for category in priority_order:
947
- if category in patterns:
948
- pattern_list = patterns[category]
949
- for pattern in pattern_list:
950
- matches = re.finditer(pattern, original_text, re.IGNORECASE | re.MULTILINE)
951
- for match in matches:
952
- if match.groups():
953
- item = match.group(1).strip()
954
- full_match = match.group(0).strip()
955
- else:
956
- item = match.group(0).strip()
957
- full_match = item
958
-
959
- # بررسی تداخل با entities قبلی
960
- overlaps = False
961
- match_start, match_end = match.span()
962
-
963
- for proc_start, proc_end in processed_entities:
964
- # بررسی تداخل موقعیت
965
- if not (match_end <= proc_start or match_start >= proc_end):
966
- overlaps = True
967
- break
968
-
969
- if (not overlaps and
970
- full_match not in found_entities and
971
- full_match not in self.mapping_table and
972
- len(full_match) >= 2):
973
-
974
- self.counters[category] += 1
975
- code = f"{category}_{self.counters[category]:03d}_REGEX"
976
- self.mapping_table[full_match] = code
977
- found_entities.add(full_match)
978
- processed_entities.add((match_start, match_end))
979
- logger.info(f"Regex ({category}): {full_match} -> {code}")
980
-
981
- # جایگزینی در متن با ترتیب طولانی‌ترین اول
982
- sorted_items = sorted(self.mapping_table.items(), key=lambda x: len(x[0]), reverse=True)
983
- for original_item, code in sorted_items:
984
- anonymized = anonymized.replace(original_item, code)
985
-
986
- logger.info(f"✅ Comprehensive anonymization completed. Found {len(self.mapping_table)} entities.")
987
- return anonymized
988
-
989
- except Exception as e:
990
- return f"❌ Error in anonymization: {str(e)}" if lang == 'en' else f"❌ خطا در ناشناس‌سازی: {str(e)}"
991
-
992
- def send_to_chatgpt(self, anonymized_text, lang='fa'):
993
- """گام 2: ارسال به ChatGPT"""
994
- try:
995
- if not anonymized_text or not anonymized_text.strip():
996
- return "❌ Anonymized text is empty!" if lang == 'en' else "❌ متن ناشناس‌شده خالی است!"
997
-
998
- if not self.api_key:
999
- return "❌ API Key not configured! Please set OPENAI_API_KEY environment variable." if lang == 'en' else "❌ کلید API تنظیم نشده است! لطفاً OPENAI_API_KEY را در متغیرهای محیطی تنظیم کنید."
1000
-
1001
- system_msg = "You are a professional financial analyst. The text contains anonymous codes. Answer questions accurately." if lang == 'en' else "شما یک تحلیلگر مالی حرفه‌ای هستید. متن حاوی کدهای ناشناس است. به سوالات با دقت پاسخ دهید."
1002
-
1003
- headers = {
1004
- "Authorization": f"Bearer {self.api_key}",
1005
- "Content-Type": "application/json"
1006
- }
1007
-
1008
- data = {
1009
- "model": "gpt-4o-mini",
1010
- "messages": [
1011
- {"role": "system", "content": system_msg},
1012
- {"role": "user", "content": anonymized_text}
1013
- ],
1014
- "max_tokens": 2000,
1015
- "temperature": 0.7
1016
- }
1017
-
1018
- response = requests.post(
1019
- "https://api.openai.com/v1/chat/completions",
1020
- headers=headers,
1021
- json=data,
1022
- timeout=30
1023
- )
1024
-
1025
- if response.status_code == 200:
1026
- result = response.json()
1027
- return result['choices'][0]['message']['content']
1028
- else:
1029
- error_data = response.json() if response.content else {}
1030
- error_message = error_data.get('error', {}).get('message', response.text)
1031
-
1032
- if 'Incorrect API key' in error_message:
1033
- return "❌ Invalid API key." if lang == 'en' else "❌ کلید API نامعتبر است."
1034
- elif 'quota' in error_message:
1035
- return "❌ API quota exceeded." if lang == 'en' else "❌ سهمیه API تمام شده است."
1036
- else:
1037
- return f"❌ API Error: {error_message}"
1038
-
1039
- except Exception as e:
1040
- return f"❌ Error connecting to ChatGPT: {str(e)}" if lang == 'en' else f"❌ خطا در ارتباط با ChatGPT: {str(e)}"
1041
-
1042
- def deanonymize_response(self, gpt_response, lang='fa'):
1043
- """گام 3: بازگردانی"""
1044
- try:
1045
- if not gpt_response or not gpt_response.strip():
1046
- return "❌ ChatGPT response is empty!" if lang == 'en' else "❌ پاسخ ChatGPT خالی است!"
1047
-
1048
- if not self.mapping_table:
1049
- return "❌ Mapping table is empty!" if lang == 'en' else "❌ جدول نگاشت خالی است!"
1050
-
1051
- final_result = gpt_response
1052
- reverse_mapping = {code: original for original, code in self.mapping_table.items()}
1053
-
1054
- sorted_codes = sorted(reverse_mapping.items(), key=lambda x: len(x[0]), reverse=True)
1055
- for code, original in sorted_codes:
1056
- final_result = final_result.replace(code, original)
1057
- escaped_code = code.replace('_', '\\_')
1058
- final_result = final_result.replace(escaped_code, original)
1059
-
1060
- return final_result
1061
-
1062
- except Exception as e:
1063
- return f"❌ Deanonymization error: {str(e)}" if lang == 'en' else f"❌ خطا در بازگردانی: {str(e)}"
1064
-
1065
- def get_model_status(self):
1066
- """وضعیت مدل‌های محلی"""
1067
- status = "🤖 **Comprehensive Anonymization System Status (Enhanced with 221 Patterns):**\n\n"
1068
-
1069
- if hasattr(self, 'model_status') and self.model_status:
1070
- for model_type, model_status in self.model_status.items():
1071
- if model_type == 'persian':
1072
- status += f"• **Persian NER**: {model_status}\n"
1073
- elif model_type == 'english':
1074
- status += f"• **English NER**: {model_status}\n"
1075
- elif model_type == 'financial':
1076
- status += f"• **Financial NER**: {model_status}\n"
1077
- elif model_type == 'transformers':
1078
- status += f"• **Transformers**: {model_status}\n"
1079
- elif model_type == 'fallback':
1080
- status += f"• **Fallback Mode**: {model_status}\n"
1081
- elif model_type == 'critical':
1082
- status += f"• **Critical**: {model_status}\n"
1083
- elif model_type == 'directory':
1084
- status += f"• **Directory**: {model_status}\n"
1085
-
1086
- loaded_count = sum(1 for status in getattr(self, 'model_status', {}).values()
1087
- if status.startswith("✅"))
1088
- status += f"\n📊 **Summary**: {loaded_count}/2 local models loaded"
1089
-
1090
- status += f"\n📁 **Models Path**: {self.models_base_path}"
1091
- status += f"\n🔧 **Latest Features**: Comprehensive 221-pattern detection system"
1092
-
1093
- status += f"\n\n🔍 **Comprehensive Sensitive Data Detection (27 Categories, 221 Patterns):**"
1094
-
1095
- # اطلاعات شخصی و هویتی
1096
- status += f"\n\n👤 **Personal & Identity Information (30 patterns):**"
1097
- status += f"\n • PERSON: نام‌ها با عناوین، سمت‌ها، ضمایر"
1098
- status += f"\n • MIXED_NAMES: نام‌های ترکیبی فارسی-انگلیسی"
1099
- status += f"\n • ID_NUMBER: شبا، کد ملی، پاسپورت، SSN، FICO"
1100
- status += f"\n • ENGLISH_TITLES: عناوین تجاری، مهندسی، مالی"
1101
-
1102
- # اطلاعات مالی
1103
- status += f"\n\n💰 **Financial Information (37 patterns):**"
1104
- status += f"\n • AMOUNT: مبالغ فارسی/انگلیسی، دلار، یورو"
1105
- status += f"\n • INTERNATIONAL_CURRENCIES: EUR، AED، GBP، CHF، JPY"
1106
- status += f"\n • ACCOUNT: حساب‌های بانکی، واریز، سود"
1107
- status += f"\n • FINANCIAL_TERMS: اصطلاحات مالی تخصصی"
1108
- status += f"\n • STOCK_SYMBOL: نمادهای بورس ایران و بین‌المللی"
1109
-
1110
- # اطلاعات زمانی
1111
- status += f"\n\n⏰ **Temporal Information (30 patterns):**"
1112
- status += f"\n • DATE: تاریخ‌های فارسی/میلادی، بازه‌های زمانی"
1113
- status += f"\n • ADVANCED_DATE_FORMATS: timestamp، timezone، fiscal year"
1114
- status += f"\n • TIME_RANGES: ساعات کاری، maintenance window"
1115
-
1116
- # اطلاعات مکانی
1117
- status += f"\n\n📍 **Location Information (14 patterns):**"
1118
- status += f"\n • LOCATION: شهرها، استان‌ها، کشورها"
1119
- status += f"\n • COMPLEX_ADDRESSES: آدرس‌های پیچیده با جزئیات"
1120
-
1121
- # اطلاعات فنی
1122
- status += f"\n\n⚙️ **Technical & Technological Information (32 patterns):**"
1123
- status += f"\n • TECHNICAL_CODES: کدهای سریال، مرجع، پروژه"
1124
- status += f"\n • NETWORK_ADDRESSES: IP، MAC، hostname، domain"
1125
- status += f"\n • TECHNICAL_UNITS: واحدهای برق، حجم، آلودگی"
1126
- status += f"\n • ACRONYMS_ABBREVIATIONS: اختصارات فنی، مالی، علمی"
1127
-
1128
- # اطلاعات کسب‌وکار
1129
- status += f"\n\n🏢 **Business Information (39 patterns):**"
1130
- status += f"\n • COMPANY: شرکت‌ها، بانک‌ها، سازمان‌ها"
1131
- status += f"\n • BUSINESS_TERMS: اصطلاحات تجاری تخصصی"
1132
- status += f"\n • PRODUCT: محصولات پتروشیمی، پلیمرها"
1133
- status += f"\n • PETROCHEMICAL: ترکیبات شیمیایی پیچیده"
1134
-
1135
- # اطلاعات کمیت
1136
- status += f"\n\n📏 **Quantity & Unit Information (26 patterns):**"
1137
- status += f"\n • PERCENTAGE: درصدها، نرخ‌های رشد"
1138
- status += f"\n • VOLUME: حجم‌ها، ظرفیت‌ها، مقادیر"
1139
- status += f"\n • RATIOS: نسبت‌ها، شاخص‌ها"
1140
-
1141
- # اطلاعات ارتباطی
1142
- status += f"\n\n📞 **Communication Information (5 patterns):**"
1143
- status += f"\n • PHONE: تلفن، موبایل، extension"
1144
- status += f"\n • EMAIL: ایمیل‌ها با کلمات کلیدی"
1145
-
1146
- status += f"\n\n✨ **Key Enhancements:**"
1147
- status += f"\n 🎯 Priority-based processing prevents overlap"
1148
- status += f"\n 🇮🇷 Full Persian/Arabic digit support (۰-۹)"
1149
- status += f"\n 🔄 Context-aware pattern matching"
1150
- status += f"\n 📊 Comprehensive 221-pattern coverage"
1151
- status += f"\n 🛡️ Maximum sensitive data protection"
1152
-
1153
- return status
1154
-
1155
- def process_all_steps(input_text, language):
1156
- """پردازش خودکار تمام مراحل"""
1157
- lang = 'en' if language == 'English' else 'fa'
1158
-
1159
- if not input_text.strip():
1160
- error_msg = "❌ Please enter input text!" if lang == 'en' else "❌ لطفاً متن ورودی را وارد کنید!"
1161
- return error_msg, "", "", ""
1162
-
1163
- try:
1164
- start_time = time.time()
1165
-
1166
- anonymized_text = anonymizer.anonymize_text(input_text, lang)
1167
- if anonymized_text.startswith("❌"):
1168
- return anonymized_text, "", "", ""
1169
-
1170
- gpt_response = anonymizer.send_to_chatgpt(anonymized_text, lang)
1171
- if gpt_response.startswith("❌"):
1172
- entities_found = len(anonymizer.mapping_table)
1173
- local_ner_count = sum(1 for code in anonymizer.mapping_table.values() if '_LOCAL_NER' in code)
1174
- regex_count = sum(1 for code in anonymizer.mapping_table.values() if '_REGEX' in code)
1175
-
1176
- # آمار اطلاعات حساس
1177
- sensitive_categories = ['ID_NUMBER', 'EMAIL', 'PHONE', 'ACCOUNT', 'TECHNICAL_CODES', 'NETWORK_ADDRESSES']
1178
- sensitive_count = sum(1 for code in anonymizer.mapping_table.values()
1179
- if any(cat in code for cat in sensitive_categories))
1180
-
1181
- method = "Comprehensive Local NER + 221 Regex Patterns" if anonymizer.models_loaded else "Comprehensive 221 Regex Patterns Only"
1182
- success_msg = (f"✅ Comprehensive anonymization completed with {method}!\n"
1183
- f"🔐 Sensitive data: {sensitive_count} | 🤖 NER: {local_ner_count} | 🔍 Regex: {regex_count}\n"
1184
- f"📊 Total: {entities_found} entities protected with 221-pattern system")
1185
- return success_msg, anonymized_text, gpt_response, ""
1186
-
1187
- final_result = anonymizer.deanonymize_response(gpt_response, lang)
1188
-
1189
- total_time = time.time() - start_time
1190
- entities_found = len(anonymizer.mapping_table)
1191
- local_ner_count = sum(1 for code in anonymizer.mapping_table.values() if '_LOCAL_NER' in code)
1192
- regex_count = sum(1 for code in anonymizer.mapping_table.values() if '_REGEX' in code)
1193
-
1194
- # آمار تفصیلی اطلاعات حساس
1195
- id_count = sum(1 for code in anonymizer.mapping_table.values() if 'ID_NUMBER' in code)
1196
- email_count = sum(1 for code in anonymizer.mapping_table.values() if 'EMAIL' in code)
1197
- phone_count = sum(1 for code in anonymizer.mapping_table.values() if 'PHONE' in code)
1198
- account_count = sum(1 for code in anonymizer.mapping_table.values() if 'ACCOUNT' in code)
1199
- tech_count = sum(1 for code in anonymizer.mapping_table.values() if 'TECHNICAL_CODES' in code)
1200
- network_count = sum(1 for code in anonymizer.mapping_table.values() if 'NETWORK_ADDRESSES' in code)
1201
-
1202
- sensitive_details = []
1203
- if id_count > 0: sensitive_details.append(f"🆔 IDs: {id_count}")
1204
- if email_count > 0: sensitive_details.append(f"📧 Emails: {email_count}")
1205
- if phone_count > 0: sensitive_details.append(f"📞 Phones: {phone_count}")
1206
- if account_count > 0: sensitive_details.append(f"🏦 Accounts: {account_count}")
1207
- if tech_count > 0: sensitive_details.append(f"⚙️ Tech: {tech_count}")
1208
- if network_count > 0: sensitive_details.append(f"🌐 Network: {network_count}")
1209
-
1210
- method = "Comprehensive Local NER + 221 Regex Patterns" if anonymizer.models_loaded else "Comprehensive 221 Regex Patterns Only"
1211
- success_msg = (f"🎉 Complete comprehensive anonymization & restoration successful!\n"
1212
- f"🔧 Method: {method}\n"
1213
- f"🔐 Sensitive data: {' | '.join(sensitive_details) if sensitive_details else '0'}\n"
1214
- f"📊 Total: {entities_found} entities | ⏱️ Time: {total_time:.2f}s | 🎯 221-pattern coverage")
1215
-
1216
- return success_msg, anonymized_text, gpt_response, final_result
1217
-
1218
- except Exception as e:
1219
- error_msg = f"❌ Processing error: {str(e)}" if lang == 'en' else f"❌ خطا در پردازش: {str(e)}"
1220
- return error_msg, "", "", ""
1221
-
1222
- def get_mapping_table(language):
1223
- """نمایش جدول نگاشت"""
1224
- lang = 'en' if language == 'English' else 'fa'
1225
-
1226
- if not anonymizer.mapping_table:
1227
- return "❌ Mapping table is empty! Please process some text first." if lang == 'en' else "❌ جدول نگاشت خالی است! ابتدا متنی را پردازش کنید."
1228
-
1229
- result = "📋 **Comprehensive 221-Pattern Sensitive Data Mapping Table:**\n\n" if lang == 'en' else "📋 **جدول نگاشت جامع اطلاعات حساس 221 الگو:**\n\n"
1230
-
1231
- local_ner_items = {k: v for k, v in anonymizer.mapping_table.items() if '_LOCAL_NER' in v}
1232
- regex_items = {k: v for k, v in anonymizer.mapping_table.items() if '_REGEX' in v}
1233
-
1234
- # گروه‌بندی بر اساس نوع اطلاعات حساس با اولویت
1235
- priority_categories = {
1236
- 'ID_NUMBER': '🆔 **Identity & Financial Codes**',
1237
- 'EMAIL': '📧 **Email Addresses**',
1238
- 'PHONE': '📞 **Phone Numbers**',
1239
- 'ACCOUNT': '🏦 **Bank Account Numbers**',
1240
- 'TECHNICAL_CODES': '⚙️ **Technical Codes & Serials**',
1241
- 'NETWORK_ADDRESSES': '🌐 **Network Addresses (IP/MAC)**'
1242
- }
1243
-
1244
- sensitive_found = False
1245
- for category, title in priority_categories.items():
1246
- category_items = {k: v for k, v in anonymizer.mapping_table.items() if category in v}
1247
- if category_items:
1248
- sensitive_found = True
1249
- result += f"{title}:\n"
1250
- for original, code in list(category_items.items())[:8]:
1251
- result += f" • `{original}` → `{code}`\n"
1252
- if len(category_items) > 8:
1253
- result += f" ... و {len(category_items) - 8} مورد دیگر\n"
1254
- result += "\n"
1255
-
1256
- if local_ner_items:
1257
- result += "🤖 **Local NER Detected**:\n"
1258
- for original, code in list(local_ner_items.items())[:8]:
1259
- result += f" • `{original}` → `{code}`\n"
1260
- if len(local_ner_items) > 8:
1261
- result += f" ... و {len(local_ner_items) - 8} مورد دیگر\n"
1262
- result += "\n"
1263
-
1264
- # سایر موارد (مالی، شرکتی و غیره)
1265
- other_categories = ['AMOUNT', 'INTERNATIONAL_CURRENCIES', 'PERCENTAGE', 'COMPANY', 'PERSON', 'STOCK_SYMBOL', 'DATE', 'ADVANCED_DATE_FORMATS', 'TIME_RANGES', 'COMPLEX_ADDRESSES', 'MIXED_NAMES', 'ENGLISH_TITLES', 'TECHNICAL_UNITS', 'ACRONYMS_ABBREVIATIONS']
1266
- other_items = {k: v for k, v in regex_items.items()
1267
- if any(cat in v for cat in other_categories)}
1268
-
1269
- if other_items:
1270
- result += "💼 **Business, Financial & Technical Data**:\n"
1271
- for original, code in list(other_items.items())[:10]:
1272
- result += f" • `{original}` → `{code}`\n"
1273
- if len(other_items) > 10:
1274
- result += f" ... و {len(other_items) - 10} مورد دیگر\n"
1275
-
1276
- # آمار کلی
1277
- sensitive_count = sum(len({k: v for k, v in anonymizer.mapping_table.items() if cat in v})
1278
- for cat in priority_categories.keys())
1279
-
1280
- result += f"\n📊 **Comprehensive Statistics**:\n"
1281
- result += f"🔐 **High-Priority Sensitive Data**: {sensitive_count} items\n"
1282
- result += f"🤖 **NER Detected**: {len(local_ner_items)} items\n"
1283
- result += f"💼 **Business & Technical Data**: {len(other_items)} items\n"
1284
- result += f"📋 **Total Protected**: {len(anonymizer.mapping_table)} entities\n"
1285
-
1286
- result += f"\n✨ **System Enhancement**: 221-pattern comprehensive detection\n"
1287
- result += f"🎯 **Coverage**: 8 major categories, 27 subcategories\n"
1288
- result += f"🛡️ **Protection Level**: Maximum sensitive data anonymization achieved!"
1289
-
1290
- return result
1291
-
1292
- def clear_all():
1293
- """پاک کردن همه"""
1294
- anonymizer.mapping_table = {}
1295
- anonymizer.counters = {key: 0 for key in anonymizer.counters.keys()}
1296
- return "", "", "", "", ""
1297
-
1298
- def update_ui_text(language):
1299
- """به‌روزرسانی متن‌های رابط کاربری"""
1300
- if language == 'English':
1301
- return {
1302
- 'title': 'Comprehensive 221-Pattern Bilingual Data Anonymization System',
1303
- 'step1': 'Input Text & Settings',
1304
- 'step2': 'Anonymized Text',
1305
- 'step3': 'Raw ChatGPT Response',
1306
- 'step4': 'Final Restored Response',
1307
- 'input_placeholder': 'Enter your original text here...\nExample: Company reports, person names, financial amounts, phone numbers, emails, IBAN codes, bank accounts, IP addresses, technical codes, timestamps, etc.\n\n✨ Comprehensive 221-pattern system protects all sensitive data types!',
1308
- 'process_btn': 'Process with Comprehensive 221-Pattern Detection',
1309
- 'clear_btn': 'Clear All',
1310
- 'mapping_btn': 'Show Comprehensive 221-Pattern Mapping Table',
1311
- 'copy_btn': 'Copy',
1312
- 'direction': 'ltr'
1313
- }
1314
- else:
1315
- return {
1316
- 'title': 'سیستم جامع ناشناس‌سازی 221 الگویی دوزبانه',
1317
- 'step1': 'متن ورودی و تنظیمات',
1318
- 'step2': 'متن ناشناس‌شده',
1319
- 'step3': 'پاسخ خام ChatGPT',
1320
- 'step4': 'پاسخ نهایی بازگردانده شده',
1321
- 'input_placeholder': 'متن اصلی خود را اینجا وارد کنید...\nمثال: گزارش‌های شرکت، نام اشخاص، مبالغ مالی، شماره تلفن، ایمیل، شماره شبا، حساب بانکی، آدرس IP، کدهای فنی، timestamp و غیره\n\n✨ سیستم جامع 221 الگویی تمام انواع اطلاعات حساس را محافظت می‌کند!',
1322
- 'process_btn': 'پردازش با تشخیص جامع 221 الگویی',
1323
- 'clear_btn': 'پاک کردن همه',
1324
- 'mapping_btn': 'نمایش جدول نگاشت جامع 221 الگویی',
1325
- 'copy_btn': 'کپی',
1326
- 'direction': 'rtl'
1327
- }
1328
-
1329
- def update_interface(language):
1330
- """تغییر رابط کاربری بر اساس زبان"""
1331
- ui_text = update_ui_text(language)
1332
- is_english = (language == 'English')
1333
-
1334
- # تغییر direction برای workflow
1335
- workflow_css = "workflow ltr" if is_english else "workflow rtl"
1336
-
1337
- return [
1338
- gr.update(value=f"<h1 style='text-align: center; color: #FFD700; font-size: 3.5em; font-weight: bold; text-shadow: 3px 3px 6px rgba(0,0,0,0.5); margin: 20px 0; background: linear-gradient(45deg, #FFD700, #FFA500); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;'>📊 {ui_text['title']}</h1>"),
1339
- gr.update(value=f"<h2 style='direction: {ui_text['direction']};'>🔍 {ui_text['step1']}</h2>"),
1340
- gr.update(placeholder=ui_text['input_placeholder'], rtl=not is_english),
1341
- gr.update(value=f"🚀 {ui_text['process_btn']}"),
1342
- gr.update(value=f"🗑️ {ui_text['clear_btn']}"),
1343
- gr.update(rtl=not is_english),
1344
- gr.update(value=f"<h2 style='direction: {ui_text['direction']};'>🎭 {ui_text['step2']}</h2>"),
1345
- gr.update(rtl=not is_english),
1346
- gr.update(value=f"<h2 style='direction: {ui_text['direction']};'>🤖 {ui_text['step3']}</h2>"),
1347
- gr.update(rtl=not is_english),
1348
- gr.update(value=f"<h2 style='direction: {ui_text['direction']};'>✅ {ui_text['step4']}</h2>"),
1349
- gr.update(rtl=not is_english),
1350
- gr.update(value=f"📋 {ui_text['mapping_btn']}"),
1351
- gr.update(rtl=not is_english),
1352
- gr.update(elem_classes=workflow_css)
1353
- ]
1354
-
1355
- # ایجاد instance
1356
- anonymizer = ComprehensiveBilingualDataAnonymizer()
1357
-
1358
- # CSS اصلاح شده برای تراز‌بندی عمودی مناسب
1359
- custom_css = """
1360
- body, .gradio-container {
1361
- font-family: 'Segoe UI', Tahoma, Arial, sans-serif !important;
1362
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
1363
- min-height: 100vh !important;
1364
- padding: 20px !important;
1365
- }
1366
-
1367
- .rtl {
1368
- direction: rtl !important;
1369
- text-align: right !important;
1370
- }
1371
-
1372
- .ltr {
1373
- direction: ltr !important;
1374
- text-align: left !important;
1375
- }
1376
-
1377
- .workflow {
1378
- display: grid !important;
1379
- grid-template-columns: 1fr 1fr 1fr 1fr !important;
1380
- gap: 25px !important;
1381
- padding: 30px !important;
1382
- align-items: start !important;
1383
- align-content: start !important;
1384
- grid-auto-rows: auto !important;
1385
- }
1386
-
1387
- .workflow > * {
1388
- align-self: start !important;
1389
- vertical-align: top !important;
1390
- margin-top: 0 !important;
1391
- }
1392
-
1393
- .workflow .gradio-column,
1394
- .workflow-column {
1395
- display: flex !important;
1396
- flex-direction: column !important;
1397
- align-items: stretch !important;
1398
- justify-content: flex-start !important;
1399
- height: auto !important;
1400
- min-height: 0 !important;
1401
- margin-top: 0 !important;
1402
- padding-top: 0 !important;
1403
- }
1404
-
1405
- .gradio-textbox {
1406
- border-radius: 10px !important;
1407
- box-shadow: 0 4px 15px rgba(0,0,0,0.1) !important;
1408
- flex-grow: 1 !important;
1409
- min-height: 380px !important;
1410
- max-height: 380px !important;
1411
- height: 380px !important;
1412
- }
1413
-
1414
- .gradio-textbox textarea {
1415
- min-height: 350px !important;
1416
- max-height: 350px !important;
1417
- height: 350px !important;
1418
- resize: vertical !important;
1419
- }
1420
-
1421
- .workflow.rtl {
1422
- direction: rtl !important;
1423
- }
1424
-
1425
- .workflow.ltr {
1426
- direction: ltr !important;
1427
- }
1428
-
1429
- h1, h2, h3 {
1430
- text-shadow: 2px 2px 4px rgba(0,0,0,0.3) !important;
1431
- margin-top: 0 !important;
1432
- margin-bottom: 10px !important;
1433
- padding-top: 0 !important;
1434
- line-height: 1.2 !important;
1435
- }
1436
-
1437
- h2 {
1438
- min-height: 40px !important;
1439
- max-height: 40px !important;
1440
- display: flex !important;
1441
- align-items: center !important;
1442
- margin-bottom: 15px !important;
1443
- }
1444
-
1445
- .status-box {
1446
- background: linear-gradient(135deg, #4CAF50, #45a049) !important;
1447
- border: 3px solid #2E7D32 !important;
1448
- border-radius: 15px !important;
1449
- padding: 15px !important;
1450
- margin: 10px 0 !important;
1451
- box-shadow: 0 8px 32px rgba(76, 175, 80, 0.3) !important;
1452
- animation: pulse 2s infinite !important;
1453
- min-height: 120px !important;
1454
- max-height: 120px !important;
1455
- }
1456
-
1457
- .status-box textarea {
1458
- background: rgba(255, 255, 255, 0.95) !important;
1459
- border: none !important;
1460
- border-radius: 10px !important;
1461
- font-weight: bold !important;
1462
- font-size: 1.1em !important;
1463
- color: #1B5E20 !important;
1464
- text-shadow: 1px 1px 2px rgba(255, 255, 255, 0.8) !important;
1465
- min-height: 80px !important;
1466
- max-height: 80px !important;
1467
- }
1468
-
1469
- @keyframes pulse {
1470
- 0% { box-shadow: 0 8px 32px rgba(76, 175, 80, 0.3); }
1471
- 50% { box-shadow: 0 8px 40px rgba(76, 175, 80, 0.6); }
1472
- 100% { box-shadow: 0 8px 32px rgba(76, 175, 80, 0.3); }
1473
- }
1474
-
1475
- .gradio-button {
1476
- border-radius: 25px !important;
1477
- font-weight: bold !important;
1478
- transition: all 0.3s ease !important;
1479
- margin: 5px 0 !important;
1480
- min-height: 50px !important;
1481
- max-height: 50px !important;
1482
- }
1483
-
1484
- .gradio-button:hover {
1485
- transform: translateY(-2px) !important;
1486
- box-shadow: 0 6px 20px rgba(0,0,0,0.2) !important;
1487
- }
1488
-
1489
- h1 {
1490
- background: linear-gradient(45deg, #FFD700, #FFA500) !important;
1491
- -webkit-background-clip: text !important;
1492
- -webkit-text-fill-color: transparent !important;
1493
- background-clip: text !important;
1494
- min-height: 80px !important;
1495
- }
1496
-
1497
- @media (max-width: 1200px) {
1498
- .workflow {
1499
- grid-template-columns: 1fr 1fr !important;
1500
- gap: 20px !important;
1501
- }
1502
- }
1503
-
1504
- @media (max-width: 768px) {
1505
- .workflow {
1506
- grid-template-columns: 1fr !important;
1507
- gap: 15px !important;
1508
- }
1509
-
1510
- .gradio-textbox {
1511
- min-height: 300px !important;
1512
- max-height: 300px !important;
1513
- height: 300px !important;
1514
- }
1515
- }
1516
-
1517
- [data-testid="textbox"]:dir(rtl) {
1518
- text-align: right !important;
1519
- direction: rtl !important;
1520
- }
1521
-
1522
- [data-testid="textbox"]:dir(ltr) {
1523
- text-align: left !important;
1524
- direction: ltr !important;
1525
- }
1526
-
1527
- .gradio-container .gradio-column {
1528
- align-self: start !important;
1529
- vertical-align: top !important;
1530
- }
1531
-
1532
- .gradio-container .gradio-row {
1533
- align-items: flex-start !important;
1534
- }
1535
-
1536
- * {
1537
- box-sizing: border-box !important;
1538
- }
1539
-
1540
- .gradio-container {
1541
- align-items: start !important;
1542
- justify-content: start !important;
1543
- }
1544
- """
1545
-
1546
- # رابط کاربری Gradio با تراز‌بندی اصلاح شده
1547
- with gr.Blocks(title="📊 Comprehensive 221-Pattern Anonymization System", theme=gr.themes.Soft(), css=custom_css) as app:
1548
-
1549
- with gr.Row():
1550
- language_selector = gr.Radio(
1551
- choices=["فارسی", "English"],
1552
- value="فارسی",
1553
- label="Language / زبان",
1554
- interactive=True
1555
- )
1556
-
1557
- with gr.Column():
1558
- title = gr.HTML("<h1 style='text-align: center; color: #FFD700; font-size: 3.5em; font-weight: bold; text-shadow: 3px 3px 6px rgba(0,0,0,0.5); margin: 20px 0; background: linear-gradient(45deg, #FFD700, #FFA500); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;'>📊 سیستم جامع ناشناس‌سازی 221 الگویی دوزبانه</h1>")
1559
-
1560
- with gr.Row(elem_classes="workflow rtl") as workflow_row:
1561
- with gr.Column(elem_classes="workflow-column"):
1562
- step1_title = gr.HTML('<h2 style="direction: rtl;">🔍 متن ورودی و تنظیمات</h2>')
1563
-
1564
- input_text = gr.Textbox(
1565
- lines=15,
1566
- placeholder="متن اصلی خود را اینجا وارد کنید...\nمثال: گزارش‌های شرکت، نام اشخاص، مبالغ مالی، شماره تلفن، ایمیل، شماره شبا، حساب بانکی، آدرس IP، کدهای فنی، timestamp و غیره\n\n✨ سیستم جامع 221 الگویی تمام انواع اطلاعات حساس را محافظت می‌کند!",
1567
- label="",
1568
- rtl=True
1569
- )
1570
-
1571
- process_btn = gr.Button("🚀 پردازش با تشخیص جامع 221 الگویی", variant="primary")
1572
- clear_btn = gr.Button("🗑️ پاک کردن همه", variant="stop")
1573
-
1574
- status = gr.Textbox(
1575
- label="وضعیت",
1576
- lines=4,
1577
- interactive=False,
1578
- rtl=True,
1579
- elem_classes=["status-box"]
1580
- )
1581
-
1582
- with gr.Column(elem_classes="workflow-column"):
1583
- step2_title = gr.HTML('<h2 style="direction: rtl;">🎭 متن ناشناس‌شده</h2>')
1584
-
1585
- anonymized_output = gr.Textbox(
1586
- lines=15,
1587
- placeholder="متن ناشناس‌شده اینجا نمایش داده می‌شود...",
1588
- label="",
1589
- interactive=False,
1590
- rtl=True
1591
- )
1592
-
1593
- with gr.Column(elem_classes="workflow-column"):
1594
- step3_title = gr.HTML('<h2 style="direction: rtl;">🤖 پاسخ خام ChatGPT</h2>')
1595
-
1596
- gpt_output = gr.Textbox(
1597
- lines=15,
1598
- placeholder="پاسخ خام ChatGPT اینجا نمایش داده می‌شود...",
1599
- label="",
1600
- interactive=False,
1601
- rtl=True
1602
- )
1603
-
1604
- with gr.Column(elem_classes="workflow-column"):
1605
- step4_title = gr.HTML('<h2 style="direction: rtl;">✅ پاسخ نهایی بازگردانده شده</h2>')
1606
-
1607
- final_output = gr.Textbox(
1608
- lines=15,
1609
- placeholder="پاسخ نهایی اینجا نمایش داده می‌شود...",
1610
- label="",
1611
- interactive=False,
1612
- rtl=True
1613
- )
1614
-
1615
- with gr.Row():
1616
- with gr.Column():
1617
- mapping_title = gr.HTML('<h2>🗂️ جدول نگاشت جامع 221 الگویی</h2>')
1618
- mapping_btn = gr.Button("📋 نمایش جدول نگاشت جامع 221 الگویی")
1619
-
1620
- mapping_output = gr.Textbox(
1621
- lines=15,
1622
- label="جدول نگاشت اطلاعات",
1623
- interactive=False,
1624
- visible=False,
1625
- rtl=True
1626
- )
1627
-
1628
- with gr.Row():
1629
- with gr.Column():
1630
- status_title = gr.HTML('<h2>⚙️ وضعیت سیستم و قابلیت‌ها</h2>')
1631
- system_status_btn = gr.Button("📊 نمایش وضعیت سیستم جامع")
1632
-
1633
- system_status_output = gr.Textbox(
1634
- lines=20,
1635
- label="وضعیت سیستم",
1636
- interactive=False,
1637
- visible=False,
1638
- rtl=True
1639
- )
1640
-
1641
- # Event handlers
1642
- language_selector.change(
1643
- fn=update_interface,
1644
- inputs=[language_selector],
1645
- outputs=[title, step1_title, input_text, process_btn, clear_btn,
1646
- status, step2_title, anonymized_output, step3_title, gpt_output,
1647
- step4_title, final_output, mapping_btn, mapping_output, workflow_row]
1648
- )
1649
-
1650
- process_btn.click(
1651
- fn=process_all_steps,
1652
- inputs=[input_text, language_selector],
1653
- outputs=[status, anonymized_output, gpt_output, final_output]
1654
- )
1655
-
1656
- clear_btn.click(
1657
- fn=clear_all,
1658
- outputs=[input_text, anonymized_output, gpt_output, final_output, status]
1659
- )
1660
-
1661
- mapping_btn.click(
1662
- fn=get_mapping_table,
1663
- inputs=[language_selector],
1664
- outputs=[mapping_output]
1665
- )
1666
-
1667
- mapping_btn.click(
1668
- fn=lambda: gr.update(visible=True),
1669
- outputs=[mapping_output]
1670
- )
1671
-
1672
- system_status_btn.click(
1673
- fn=lambda: anonymizer.get_model_status(),
1674
- outputs=[system_status_output]
1675
- )
1676
-
1677
- system_status_btn.click(
1678
- fn=lambda: gr.update(visible=True),
1679
- outputs=[system_status_output]
1680
- )
1681
-
1682
- if __name__ == "__main__":
1683
- # نمایش اطلاعات سیستم در startup
1684
- print("\n" + "="*80)
1685
- print("🚀 COMPREHENSIVE 221-PATTERN BILINGUAL DATA ANONYMIZATION SYSTEM")
1686
- print("="*80)
1687
- print("📊 System Features:")
1688
- print(" • 27 comprehensive categories with 221 detection patterns")
1689
- print(" • Priority-based sensitive data protection")
1690
- print(" • Bilingual support (Persian/English)")
1691
- print(" • Local NER + Advanced Regex processing")
1692
- print(" • OpenAI ChatGPT integration")
1693
- print(" • Complete anonymization-restoration workflow")
1694
- print("\n🔐 Protected Data Types:")
1695
- print(" • Personal Identity (30 patterns)")
1696
- print(" • Financial Information (37 patterns)")
1697
- print(" • Temporal Information (30 patterns)")
1698
- print(" • Location Information (14 patterns)")
1699
- print(" • Technical & Technological (32 patterns)")
1700
- print(" • Business Information (39 patterns)")
1701
- print(" • Quantity & Unit Information (26 patterns)")
1702
- print(" • Communication Information (5 patterns)")
1703
- print("\n⚙️ Enhanced Features:")
1704
- print(" • Context-aware pattern matching")
1705
- print(" • Overlap prevention system")
1706
- print(" • Persian/Arabic digit support")
1707
- print(" • Comprehensive technical code detection")
1708
- print(" • International currency support")
1709
- print(" • Advanced timestamp recognition")
1710
- print("="*80)
1711
-
1712
- app.launch(
1713
- share=True,
1714
- server_name="0.0.0.0",
1715
- server_port=7860,
1716
- show_error=True,
1717
- favicon_path=None,
1718
- ssl_verify=False
1719
- )