leilaghomashchi commited on
Commit
8ea68e9
·
verified ·
1 Parent(s): 624693a

Upload enhanced_anonymization_app (4).py

Browse files
Files changed (1) hide show
  1. enhanced_anonymization_app (4).py +1719 -0
enhanced_anonymization_app (4).py ADDED
@@ -0,0 +1,1719 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ )