leilaghomashchi commited on
Commit
9079939
·
verified ·
1 Parent(s): bff9a74

Upload text_anonymizer (1).py

Browse files
Files changed (1) hide show
  1. text_anonymizer (1).py +355 -0
text_anonymizer (1).py ADDED
@@ -0,0 +1,355 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import pandas as pd
3
+ import re
4
+ from collections import defaultdict
5
+ import io
6
+
7
+ class TextAnonymizer:
8
+ def __init__(self):
9
+ self.person_counter = 0
10
+ self.company_counter = 0
11
+ self.amount_counter = 0
12
+ self.percent_counter = 0
13
+
14
+ # دیکشنری برای نگه‌داری تبدیل‌ها
15
+ self.person_mapping = {}
16
+ self.company_mapping = {}
17
+ self.amount_mapping = {}
18
+ self.percent_mapping = {}
19
+
20
+ def reset_counters(self):
21
+ """بازنشانی شمارنده‌ها برای پردازش جدید"""
22
+ self.person_counter = 0
23
+ self.company_counter = 0
24
+ self.amount_counter = 0
25
+ self.percent_counter = 0
26
+ self.person_mapping.clear()
27
+ self.company_mapping.clear()
28
+ self.amount_mapping.clear()
29
+ self.percent_mapping.clear()
30
+
31
+ def detect_financial_amounts(self, text):
32
+ """تشخیص مبالغ مالی"""
33
+ patterns = [
34
+ r'\$[\d,]+(?:\.\d{2})?', # $1,000.00
35
+ r'[\d,]+\s*(?:dollars?|USD|usd|Dollars?)', # 1000 dollars
36
+ r'[\d,]+\s*(?:million|billion|thousand|Million|Billion|Thousand)', # 1 million
37
+ r'[\d,]+(?:\.\d+)?\s*(?:M|B|K|m|b|k)', # 1.5M, 2B, 500K
38
+ r'€[\d,]+(?:\.\d{2})?', # €1,000.00
39
+ r'£[\d,]+(?:\.\d{2})?', # £1,000.00
40
+ ]
41
+
42
+ amounts = []
43
+ for pattern in patterns:
44
+ matches = re.finditer(pattern, text, re.IGNORECASE)
45
+ for match in matches:
46
+ amounts.append((match.start(), match.end(), match.group()))
47
+
48
+ return amounts
49
+
50
+ def detect_percentages(self, text):
51
+ """تشخیص درصدها"""
52
+ pattern = r'\d+(?:\.\d+)?%'
53
+ percentages = []
54
+ matches = re.finditer(pattern, text)
55
+ for match in matches:
56
+ percentages.append((match.start(), match.end(), match.group()))
57
+
58
+ return percentages
59
+
60
+ def detect_names_regex(self, text):
61
+ """تشخیص اسامی با regex (بدون spaCy)"""
62
+ patterns = [
63
+ r'\b[A-Z][a-z]+ [A-Z][a-z]+\b', # John Smith
64
+ r'\b[A-Z][a-z]+ [A-Z]\. [A-Z][a-z]+\b', # John M. Smith
65
+ r'\b[A-Z][a-z]+ [A-Z][a-z]+ [A-Z][a-z]+\b', # John Michael Smith
66
+ r'\bMr\. [A-Z][a-z]+\b', # Mr. Smith
67
+ r'\bMs\. [A-Z][a-z]+\b', # Ms. Johnson
68
+ r'\bDr\. [A-Z][a-z]+\b', # Dr. Brown
69
+ ]
70
+
71
+ names = []
72
+ for pattern in patterns:
73
+ matches = re.finditer(pattern, text)
74
+ for match in matches:
75
+ names.append((match.start(), match.end(), match.group()))
76
+
77
+ return names
78
+
79
+ def detect_companies_regex(self, text):
80
+ """تشخیص شرکت‌ها با regex"""
81
+ patterns = [
82
+ r'\b[A-Z][a-z]+ (?:Inc|Corp|LLC|Ltd|Company|Co|Corporation|Group|Technologies|Tech|Systems|Solutions|Services|International|Global|Enterprises)\.?\b',
83
+ r'\b(?:Apple|Microsoft|Google|Amazon|Facebook|Meta|Netflix|Tesla|Oracle|IBM|Intel|Cisco|Adobe|Salesforce|PayPal|Uber|Airbnb|Twitter|LinkedIn|Samsung|Sony|LG|HP|Dell|Lenovo|Huawei|Xiaomi|OnePlus|NVIDIA|AMD|Qualcomm|Broadcom|Texas Instruments|Micron|SK Hynix|TSMC|ASML|Shopify|Square|Stripe|Zoom|Slack|Dropbox|Spotify|Pinterest|Snapchat|TikTok|ByteDance|Alibaba|Tencent|Baidu|JD|Meituan|Didi|WeChat|Ant Group|Ping An|ICBC|China Mobile|China Telecom|China Unicom|SoftBank|NTT|KDDI|Rakuten|Nintendo|Panasonic|Canon|Nikon|Olympus|Fujifilm|Sharp|Toshiba|Hitachi|Mitsubishi|Suzuki|Nissan|Toyota|Honda|Mazda|Subaru|Yamaha|Kawasaki|Bridgestone|Michelin|Goodyear|Continental|Bosch|Siemens|SAP|Volkswagen|BMW|Mercedes|Audi|Porsche|Ferrari|Lamborghini|Rolls Royce|Bentley|Aston Martin|McLaren|Bugatti|Koenigsegg|Pagani|Maserati|Alfa Romeo|Fiat|Peugeot|Renault|Citroen|Opel|Volvo|Saab|IKEA|H&M|Zara|Uniqlo|Nike|Adidas|Puma|Under Armour|Lululemon|Patagonia|North Face|Columbia|REI|Dick's|Foot Locker|Finish Line|JD Sports|Decathlon|Walmart|Target|Costco|Home Depot|Lowe's|Best Buy|GameStop|Barnes & Noble|Starbucks|McDonald's|Burger King|KFC|Pizza Hut|Domino's|Subway|Taco Bell|Chipotle|Panera|Dunkin|Tim Hortons|Costa|Nescafe|Coca Cola|Pepsi|Red Bull|Monster|Gatorade|Powerade|Vitamin Water|Smartwater|Evian|Perrier|San Pellegrino|Fiji|Dasani|Aquafina|Sprite|Fanta|Dr Pepper|Mountain Dew|7UP|Schweppes|Heineken|Budweiser|Corona|Stella Artois|Guinness|Beck's|Carlsberg|Tuborg|Amstel|Kronenbourg|Peroni|Moretti|Nastro Azzurro|Blue Moon|Coors|Miller|Bud Light|Michelob|Samuel Adams|Sierra Nevada|New Belgium|Dogfish Head|Stone|Lagunitas|Ballast Point|Anchor|Founders|Bell's|Great Lakes|Deschutes|Widmer|Rogue|Ninkasi|Hopworks|Bridgeport|Full Sail|Deschutes|Bend|10 Barrel|Crux|Boneyard|Goodlife|Worthy|Sunriver|Pelican|Rogue|Hair of the Dog|Upright|Cascade|Commons|Base Camp|Migration|StormBreaker|Culmination|Great Notion|Modern Times|Other Half|Tree House|Trillium|The Alchemist|Hill Farmstead|Lawson's Finest|The Veil|Monkish|Russian River|Pliny|Blind Pig|Row 2|Hill 2|Knee Deep|Auburn|Device|Moksa|Eight Bridges|Knee Deep|Abnormal|Fieldwork|New Glory|Fort Point|Cellarmaker|Toronado|Magnolia|Beach Chalet|Golden Gate Park|Ocean Beach|Land's End|Cliff House|Sutro Baths|Baker Beach|Presidio|Crissy Field|Marina|Fisherman's Wharf|Pier 39|Alcatraz|Angel Island|Treasure Island|Bay Bridge|Golden Gate Bridge|Twin Peaks|Mission Dolores|Castro|Haight Ashbury|North Beach|Chinatown|Union Square|Financial District|SOMA|Mission Bay|Potrero Hill|Dogpatch|Bernal Heights|Noe Valley|Glen Park|Excelsior|Sunset|Richmond|Avenues|Parkside|West Portal|Forest Hill|St Francis Wood|Sea Cliff|Pacific Heights|Russian Hill|Nob Hill|Telegraph Hill|North Beach|Lombard Street|Coit Tower|Transamerica Pyramid|Salesforce Tower|555 California|Bank of America|Wells Fargo|Chase|Citibank|US Bank|PNC|Capital One|Ally|American Express|Discover|Mastercard|Visa|JPMorgan|Goldman Sachs|Morgan Stanley|Bank of America|Merrill Lynch|Charles Schwab|Fidelity|Vanguard|BlackRock|State Street|T Rowe Price|Franklin Templeton|Invesco|Northern Trust|BNY Mellon|Credit Suisse|UBS|Deutsche Bank|Barclays|HSBC|Standard Chartered|RBS|Lloyds|Santander|BBVA|BNP Paribas|Societe Generale|Credit Agricole|ING|ABN AMRO|Rabobank|Danske Bank|Nordea|SEB|Handelsbanken|Swedbank|DNB|SpareBank|Storebrand)\\b',
84
+ ]
85
+
86
+ companies = []
87
+ for pattern in patterns:
88
+ matches = re.finditer(pattern, text, re.IGNORECASE)
89
+ for match in matches:
90
+ companies.append((match.start(), match.end(), match.group()))
91
+
92
+ return companies
93
+
94
+ def anonymize_text(self, text):
95
+ """ناشناس‌سازی متن با regex"""
96
+ if not text or pd.isna(text):
97
+ return text
98
+
99
+ replacements = []
100
+
101
+ # تشخیص اسامی اشخاص
102
+ names = self.detect_names_regex(text)
103
+ for start, end, name in names:
104
+ if name not in self.person_mapping:
105
+ self.person_counter += 1
106
+ self.person_mapping[name] = f"person-{self.person_counter:02d}"
107
+ replacements.append((start, end, self.person_mapping[name]))
108
+
109
+ # تشخیص شرکت‌ها
110
+ companies = self.detect_companies_regex(text)
111
+ for start, end, company in companies:
112
+ if company not in self.company_mapping:
113
+ self.company_counter += 1
114
+ self.company_mapping[company] = f"company-{self.company_counter:02d}"
115
+ replacements.append((start, end, self.company_mapping[company]))
116
+
117
+ # تشخیص مبالغ مالی
118
+ amounts = self.detect_financial_amounts(text)
119
+ for start, end, amount in amounts:
120
+ if amount not in self.amount_mapping:
121
+ self.amount_counter += 1
122
+ self.amount_mapping[amount] = f"amount-{self.amount_counter:02d}"
123
+ replacements.append((start, end, self.amount_mapping[amount]))
124
+
125
+ # تشخیص درصدها
126
+ percentages = self.detect_percentages(text)
127
+ for start, end, percent in percentages:
128
+ if percent not in self.percent_mapping:
129
+ self.percent_counter += 1
130
+ self.percent_mapping[percent] = f"percent-{self.percent_counter:02d}"
131
+ replacements.append((start, end, self.percent_mapping[percent]))
132
+
133
+ # حذف تداخل‌ها و مرتب‌سازی
134
+ replacements = self.remove_overlaps(replacements)
135
+ replacements.sort(key=lambda x: x[0], reverse=True)
136
+
137
+ # اعمال جایگزینی‌ها
138
+ result = text
139
+ for start, end, replacement in replacements:
140
+ result = result[:start] + replacement + result[end:]
141
+
142
+ return result
143
+
144
+ def remove_overlaps(self, replacements):
145
+ """حذف تداخل‌ها در جایگزینی‌ها"""
146
+ if not replacements:
147
+ return []
148
+
149
+ # مرتب‌سازی بر اساس موقعیت شروع
150
+ replacements.sort(key=lambda x: x[0])
151
+
152
+ filtered = []
153
+ for start, end, replacement in replacements:
154
+ # بررسی تداخل با آخرین جایگزینی اضافه شده
155
+ if not filtered or start >= filtered[-1][1]:
156
+ filtered.append((start, end, replacement))
157
+
158
+ return filtered
159
+
160
+ def get_mapping_summary(self):
161
+ """خلاصه‌ای از تبدیل‌های انجام شده"""
162
+ summary = []
163
+
164
+ if self.person_mapping:
165
+ summary.append("**اسامی اشخاص:**")
166
+ for original, anonymized in self.person_mapping.items():
167
+ summary.append(f"- {original} → {anonymized}")
168
+ summary.append("")
169
+
170
+ if self.company_mapping:
171
+ summary.append("**نام شرکت‌ها:**")
172
+ for original, anonymized in self.company_mapping.items():
173
+ summary.append(f"- {original} → {anonymized}")
174
+ summary.append("")
175
+
176
+ if self.amount_mapping:
177
+ summary.append("**مبالغ مالی:**")
178
+ for original, anonymized in self.amount_mapping.items():
179
+ summary.append(f"- {original} → {anonymized}")
180
+ summary.append("")
181
+
182
+ if self.percent_mapping:
183
+ summary.append("**درصدها:**")
184
+ for original, anonymized in self.percent_mapping.items():
185
+ summary.append(f"- {original} → {anonymized}")
186
+
187
+ return "\n".join(summary) if summary else "هیچ موجودیت حساسی یافت نشد."
188
+
189
+ # ایجاد نمونه از کلاس ناشناس‌ساز
190
+ anonymizer = TextAnonymizer()
191
+
192
+ def process_csv(file):
193
+ """پردازش فایل CSV"""
194
+ try:
195
+ # بازنشانی شمارنده‌ها
196
+ anonymizer.reset_counters()
197
+
198
+ # بررسی فایل
199
+ if file is None:
200
+ return None, "لطفاً فایل CSV آپلود کنید.", "", None
201
+
202
+ # خواندن فایل CSV
203
+ if file.name.endswith('.csv'):
204
+ df = pd.read_csv(file.name)
205
+ else:
206
+ return None, "لطفاً فایل CSV آپلود کنید.", "", None
207
+
208
+ # بررسی وجود ستون original_text
209
+ if 'original_text' not in df.columns:
210
+ available_columns = ', '.join(df.columns.tolist())
211
+ return None, f"ستون 'original_text' در فایل یافت نشد. ستون‌های موجود: {available_columns}", "", None
212
+
213
+ # ایجاد کپی از دیتافریم
214
+ result_df = df.copy()
215
+
216
+ # ناشناس‌سازی متن‌های ستون original_text
217
+ result_df['anonymized_text'] = df['original_text'].apply(anonymizer.anonymize_text)
218
+
219
+ # تبدیل به CSV برای دانلود
220
+ output = io.StringIO()
221
+ result_df.to_csv(output, index=False, encoding='utf-8')
222
+ csv_content = output.getvalue()
223
+
224
+ # ایجاد فایل CSV برای دانلود
225
+ output_file = "anonymized_data.csv"
226
+ with open(output_file, 'w', encoding='utf-8') as f:
227
+ f.write(csv_content)
228
+
229
+ # نمایش نمونه از نتایج
230
+ sample_df = result_df[['original_text', 'anonymized_text']].head(10)
231
+
232
+ # خلاصه تبدیل‌ها
233
+ mapping_summary = anonymizer.get_mapping_summary()
234
+
235
+ return output_file, f"✅ پردازش کامل شد! {len(df)} ردیف پردازش شد.", mapping_summary, sample_df
236
+
237
+ except Exception as e:
238
+ return None, f"❌ خطا در پردازش فایل: {str(e)}", "", None
239
+
240
+ def process_single_text(text):
241
+ """پردازش تک متن"""
242
+ if not text.strip():
243
+ return "", "لطفاً متنی وارد کنید."
244
+
245
+ anonymizer.reset_counters()
246
+ anonymized = anonymizer.anonymize_text(text)
247
+ mapping_summary = anonymizer.get_mapping_summary()
248
+
249
+ return anonymized, mapping_summary
250
+
251
+ # ایجاد رابط کاربری Gradio
252
+ with gr.Blocks(title="ناشناس‌سازی متن", theme=gr.themes.Soft()) as demo:
253
+ gr.Markdown("""
254
+ # 🔒 برنامه ناشناس‌سازی متن (نسخه Regex)
255
+
256
+ ⚡ **وضعیت:** حالت سریع - بدون نیاز به spaCy
257
+
258
+ این برنامه متن‌های شما را ناشناس می‌کند و اطلاعات حساس زیر را جایگزین می‌کند:
259
+ - 👤 **اسامی اشخاص** → person-01, person-02, ...
260
+ - 🏢 **نام شرکت‌ها** → company-01, company-02, ...
261
+ - 💰 **مبالغ مالی** → amount-01, amount-02, ...
262
+ - 📊 **درصدها** → percent-01, percent-02, ...
263
+
264
+ **نسخه ۱:** آدرس‌ها، مکان‌ها و تاریخ‌ها ناشناس‌سازی نمی‌شوند.
265
+ """)
266
+
267
+ with gr.Tabs():
268
+ # تب پردازش فایل CSV
269
+ with gr.TabItem("📁 پردازش فایل CSV"):
270
+ gr.Markdown("### آپلود فایل CSV با ستون 'original_text'")
271
+
272
+ with gr.Row():
273
+ with gr.Column():
274
+ file_input = gr.File(
275
+ label="فایل CSV را انتخاب کنید",
276
+ file_types=[".csv"],
277
+ type="filepath"
278
+ )
279
+ process_btn = gr.Button("🚀 شروع پردازش", variant="primary")
280
+
281
+ with gr.Column():
282
+ status_output = gr.Textbox(
283
+ label="وضعیت",
284
+ interactive=False
285
+ )
286
+ download_file = gr.File(
287
+ label="دانلود فایل ناشناس‌سازی شده",
288
+ interactive=False
289
+ )
290
+
291
+ with gr.Row():
292
+ with gr.Column():
293
+ mapping_output = gr.Markdown(
294
+ label="خلاصه تبدیل‌ها",
295
+ value="خلاصه تبدیل‌ها اینجا نمایش داده می‌شود..."
296
+ )
297
+
298
+ with gr.Column():
299
+ sample_output = gr.Dataframe(
300
+ label="نمونه نتایج (۱۰ ردیف اول)",
301
+ interactive=False
302
+ )
303
+
304
+ # تب تست تک متن
305
+ with gr.TabItem("📝 تست تک متن"):
306
+ gr.Markdown("### تست ناشناس‌سازی روی یک متن")
307
+
308
+ with gr.Row():
309
+ with gr.Column():
310
+ text_input = gr.Textbox(
311
+ label="متن اصلی",
312
+ placeholder="متن خود را اینجا وارد کنید...",
313
+ lines=5
314
+ )
315
+ test_btn = gr.Button("🔍 ناشناس‌سازی", variant="primary")
316
+
317
+ with gr.Column():
318
+ text_output = gr.Textbox(
319
+ label="متن ناشناس‌سازی شده",
320
+ lines=5,
321
+ interactive=False
322
+ )
323
+ text_mapping = gr.Markdown(
324
+ label="تبدیل‌های انجام شده"
325
+ )
326
+
327
+ # اتصال رویدادها
328
+ process_btn.click(
329
+ fn=process_csv,
330
+ inputs=[file_input],
331
+ outputs=[download_file, status_output, mapping_output, sample_output]
332
+ )
333
+
334
+ test_btn.click(
335
+ fn=process_single_text,
336
+ inputs=[text_input],
337
+ outputs=[text_output, text_mapping]
338
+ )
339
+
340
+ # مثال در بخش تست
341
+ gr.Examples(
342
+ examples=[
343
+ ["John Smith works at Microsoft and earned $50,000 with a 15% bonus."],
344
+ ["Sarah Johnson from Google Inc. reported revenues of $2.5 million, representing a 25% increase."],
345
+ ["The CEO of Apple, Tim Cook, announced profits of $1.2B with 18.5% growth rate."],
346
+ ["Dr. Michael Brown from IBM Corp. received €75,000 salary increase of 12%."],
347
+ ["Ms. Lisa Wilson at Amazon reported quarterly results of £500K with 8.7% margin."]
348
+ ],
349
+ inputs=[text_input],
350
+ label="نمونه متن‌ها"
351
+ )
352
+
353
+ # اجرای برنامه
354
+ if __name__ == "__main__":
355
+ demo.launch()