Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import pandas as pd | |
| import re | |
| from collections import defaultdict | |
| import io | |
| class TextAnonymizer: | |
| def __init__(self): | |
| self.person_counter = 0 | |
| self.company_counter = 0 | |
| self.amount_counter = 0 | |
| self.percent_counter = 0 | |
| # دیکشنری برای نگهداری تبدیلها | |
| self.person_mapping = {} | |
| self.company_mapping = {} | |
| self.amount_mapping = {} | |
| self.percent_mapping = {} | |
| def reset_counters(self): | |
| """بازنشانی شمارندهها برای پردازش جدید""" | |
| self.person_counter = 0 | |
| self.company_counter = 0 | |
| self.amount_counter = 0 | |
| self.percent_counter = 0 | |
| self.person_mapping.clear() | |
| self.company_mapping.clear() | |
| self.amount_mapping.clear() | |
| self.percent_mapping.clear() | |
| def detect_financial_amounts(self, text): | |
| """تشخیص مبالغ مالی""" | |
| patterns = [ | |
| r'\$[\d,]+(?:\.\d{2})?', # $1,000.00 | |
| r'[\d,]+\s*(?:dollars?|USD|usd|Dollars?)', # 1000 dollars | |
| r'[\d,]+\s*(?:million|billion|thousand|Million|Billion|Thousand)', # 1 million | |
| r'[\d,]+(?:\.\d+)?\s*(?:M|B|K|m|b|k)', # 1.5M, 2B, 500K | |
| r'€[\d,]+(?:\.\d{2})?', # €1,000.00 | |
| r'£[\d,]+(?:\.\d{2})?', # £1,000.00 | |
| ] | |
| amounts = [] | |
| for pattern in patterns: | |
| matches = re.finditer(pattern, text, re.IGNORECASE) | |
| for match in matches: | |
| amounts.append((match.start(), match.end(), match.group())) | |
| return amounts | |
| def detect_percentages(self, text): | |
| """تشخیص درصدها""" | |
| pattern = r'\d+(?:\.\d+)?%' | |
| percentages = [] | |
| matches = re.finditer(pattern, text) | |
| for match in matches: | |
| percentages.append((match.start(), match.end(), match.group())) | |
| return percentages | |
| def detect_names_regex(self, text): | |
| """تشخیص اسامی با regex (بدون spaCy)""" | |
| patterns = [ | |
| r'\b[A-Z][a-z]+ [A-Z][a-z]+\b', # John Smith | |
| r'\b[A-Z][a-z]+ [A-Z]\. [A-Z][a-z]+\b', # John M. Smith | |
| r'\b[A-Z][a-z]+ [A-Z][a-z]+ [A-Z][a-z]+\b', # John Michael Smith | |
| r'\bMr\. [A-Z][a-z]+\b', # Mr. Smith | |
| r'\bMs\. [A-Z][a-z]+\b', # Ms. Johnson | |
| r'\bDr\. [A-Z][a-z]+\b', # Dr. Brown | |
| ] | |
| names = [] | |
| for pattern in patterns: | |
| matches = re.finditer(pattern, text) | |
| for match in matches: | |
| names.append((match.start(), match.end(), match.group())) | |
| return names | |
| def detect_companies_regex(self, text): | |
| """تشخیص شرکتها با regex""" | |
| patterns = [ | |
| r'\b[A-Z][a-z]+ (?:Inc|Corp|LLC|Ltd|Company|Co|Corporation|Group|Technologies|Tech|Systems|Solutions|Services|International|Global|Enterprises)\.?\b', | |
| 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', | |
| ] | |
| companies = [] | |
| for pattern in patterns: | |
| matches = re.finditer(pattern, text, re.IGNORECASE) | |
| for match in matches: | |
| companies.append((match.start(), match.end(), match.group())) | |
| return companies | |
| def anonymize_text(self, text): | |
| """ناشناسسازی متن با regex""" | |
| if not text or pd.isna(text): | |
| return text | |
| replacements = [] | |
| # تشخیص اسامی اشخاص | |
| names = self.detect_names_regex(text) | |
| for start, end, name in names: | |
| if name not in self.person_mapping: | |
| self.person_counter += 1 | |
| self.person_mapping[name] = f"person-{self.person_counter:02d}" | |
| replacements.append((start, end, self.person_mapping[name])) | |
| # تشخیص شرکتها | |
| companies = self.detect_companies_regex(text) | |
| for start, end, company in companies: | |
| if company not in self.company_mapping: | |
| self.company_counter += 1 | |
| self.company_mapping[company] = f"company-{self.company_counter:02d}" | |
| replacements.append((start, end, self.company_mapping[company])) | |
| # تشخیص مبالغ مالی | |
| amounts = self.detect_financial_amounts(text) | |
| for start, end, amount in amounts: | |
| if amount not in self.amount_mapping: | |
| self.amount_counter += 1 | |
| self.amount_mapping[amount] = f"amount-{self.amount_counter:02d}" | |
| replacements.append((start, end, self.amount_mapping[amount])) | |
| # تشخیص درصدها | |
| percentages = self.detect_percentages(text) | |
| for start, end, percent in percentages: | |
| if percent not in self.percent_mapping: | |
| self.percent_counter += 1 | |
| self.percent_mapping[percent] = f"percent-{self.percent_counter:02d}" | |
| replacements.append((start, end, self.percent_mapping[percent])) | |
| # حذف تداخلها و مرتبسازی | |
| replacements = self.remove_overlaps(replacements) | |
| replacements.sort(key=lambda x: x[0], reverse=True) | |
| # اعمال جایگزینیها | |
| result = text | |
| for start, end, replacement in replacements: | |
| result = result[:start] + replacement + result[end:] | |
| return result | |
| def remove_overlaps(self, replacements): | |
| """حذف تداخلها در جایگزینیها""" | |
| if not replacements: | |
| return [] | |
| # مرتبسازی بر اساس موقعیت شروع | |
| replacements.sort(key=lambda x: x[0]) | |
| filtered = [] | |
| for start, end, replacement in replacements: | |
| # بررسی تداخل با آخرین جایگزینی اضافه شده | |
| if not filtered or start >= filtered[-1][1]: | |
| filtered.append((start, end, replacement)) | |
| return filtered | |
| def get_mapping_summary(self): | |
| """خلاصهای از تبدیلهای انجام شده""" | |
| summary = [] | |
| if self.person_mapping: | |
| summary.append("**اسامی اشخاص:**") | |
| for original, anonymized in self.person_mapping.items(): | |
| summary.append(f"- {original} → {anonymized}") | |
| summary.append("") | |
| if self.company_mapping: | |
| summary.append("**نام شرکتها:**") | |
| for original, anonymized in self.company_mapping.items(): | |
| summary.append(f"- {original} → {anonymized}") | |
| summary.append("") | |
| if self.amount_mapping: | |
| summary.append("**مبالغ مالی:**") | |
| for original, anonymized in self.amount_mapping.items(): | |
| summary.append(f"- {original} → {anonymized}") | |
| summary.append("") | |
| if self.percent_mapping: | |
| summary.append("**درصدها:**") | |
| for original, anonymized in self.percent_mapping.items(): | |
| summary.append(f"- {original} → {anonymized}") | |
| return "\n".join(summary) if summary else "هیچ موجودیت حساسی یافت نشد." | |
| # ایجاد نمونه از کلاس ناشناسساز | |
| anonymizer = TextAnonymizer() | |
| def process_csv(file): | |
| """پردازش فایل CSV""" | |
| try: | |
| # بازنشانی شمارندهها | |
| anonymizer.reset_counters() | |
| # بررسی فایل | |
| if file is None: | |
| return None, "لطفاً فایل CSV آپلود کنید.", "", None | |
| # خواندن فایل CSV | |
| if file.name.endswith('.csv'): | |
| df = pd.read_csv(file.name) | |
| else: | |
| return None, "لطفاً فایل CSV آپلود کنید.", "", None | |
| # بررسی وجود ستون original_text | |
| if 'original_text' not in df.columns: | |
| available_columns = ', '.join(df.columns.tolist()) | |
| return None, f"ستون 'original_text' در فایل یافت نشد. ستونهای موجود: {available_columns}", "", None | |
| # ایجاد کپی از دیتافریم | |
| result_df = df.copy() | |
| # ناشناسسازی متنهای ستون original_text | |
| result_df['anonymized_text'] = df['original_text'].apply(anonymizer.anonymize_text) | |
| # تبدیل به CSV برای دانلود | |
| output = io.StringIO() | |
| result_df.to_csv(output, index=False, encoding='utf-8') | |
| csv_content = output.getvalue() | |
| # ایجاد فایل CSV برای دانلود | |
| output_file = "anonymized_data.csv" | |
| with open(output_file, 'w', encoding='utf-8') as f: | |
| f.write(csv_content) | |
| # نمایش نمونه از نتایج | |
| sample_df = result_df[['original_text', 'anonymized_text']].head(10) | |
| # خلاصه تبدیلها | |
| mapping_summary = anonymizer.get_mapping_summary() | |
| return output_file, f"✅ پردازش کامل شد! {len(df)} ردیف پردازش شد.", mapping_summary, sample_df | |
| except Exception as e: | |
| return None, f"❌ خطا در پردازش فایل: {str(e)}", "", None | |
| def process_single_text(text): | |
| """پردازش تک متن""" | |
| if not text.strip(): | |
| return "", "لطفاً متنی وارد کنید." | |
| anonymizer.reset_counters() | |
| anonymized = anonymizer.anonymize_text(text) | |
| mapping_summary = anonymizer.get_mapping_summary() | |
| return anonymized, mapping_summary | |
| # ایجاد رابط کاربری Gradio | |
| with gr.Blocks(title="ناشناسسازی متن", theme=gr.themes.Soft()) as demo: | |
| gr.Markdown(""" | |
| # 🔒 برنامه ناشناسسازی متن (نسخه Regex) | |
| ⚡ **وضعیت:** حالت سریع - بدون نیاز به spaCy | |
| این برنامه متنهای شما را ناشناس میکند و اطلاعات حساس زیر را جایگزین میکند: | |
| - 👤 **اسامی اشخاص** → person-01, person-02, ... | |
| - 🏢 **نام شرکتها** → company-01, company-02, ... | |
| - 💰 **مبالغ مالی** → amount-01, amount-02, ... | |
| - 📊 **درصدها** → percent-01, percent-02, ... | |
| **نسخه ۱:** آدرسها، مکانها و تاریخها ناشناسسازی نمیشوند. | |
| """) | |
| with gr.Tabs(): | |
| # تب پردازش فایل CSV | |
| with gr.TabItem("📁 پردازش فایل CSV"): | |
| gr.Markdown("### آپلود فایل CSV با ستون 'original_text'") | |
| with gr.Row(): | |
| with gr.Column(): | |
| file_input = gr.File( | |
| label="فایل CSV را انتخاب کنید", | |
| file_types=[".csv"], | |
| type="filepath" | |
| ) | |
| process_btn = gr.Button("🚀 شروع پردازش", variant="primary") | |
| with gr.Column(): | |
| status_output = gr.Textbox( | |
| label="وضعیت", | |
| interactive=False | |
| ) | |
| download_file = gr.File( | |
| label="دانلود فایل ناشناسسازی شده", | |
| interactive=False | |
| ) | |
| with gr.Row(): | |
| with gr.Column(): | |
| mapping_output = gr.Markdown( | |
| label="خلاصه تبدیلها", | |
| value="خلاصه تبدیلها اینجا نمایش داده میشود..." | |
| ) | |
| with gr.Column(): | |
| sample_output = gr.Dataframe( | |
| label="نمونه نتایج (۱۰ ردیف اول)", | |
| interactive=False | |
| ) | |
| # تب تست تک متن | |
| with gr.TabItem("📝 تست تک متن"): | |
| gr.Markdown("### تست ناشناسسازی روی یک متن") | |
| with gr.Row(): | |
| with gr.Column(): | |
| text_input = gr.Textbox( | |
| label="متن اصلی", | |
| placeholder="متن خود را اینجا وارد کنید...", | |
| lines=5 | |
| ) | |
| test_btn = gr.Button("🔍 ناشناسسازی", variant="primary") | |
| with gr.Column(): | |
| text_output = gr.Textbox( | |
| label="متن ناشناسسازی شده", | |
| lines=5, | |
| interactive=False | |
| ) | |
| text_mapping = gr.Markdown( | |
| label="تبدیلهای انجام شده" | |
| ) | |
| # اتصال رویدادها | |
| process_btn.click( | |
| fn=process_csv, | |
| inputs=[file_input], | |
| outputs=[download_file, status_output, mapping_output, sample_output] | |
| ) | |
| test_btn.click( | |
| fn=process_single_text, | |
| inputs=[text_input], | |
| outputs=[text_output, text_mapping] | |
| ) | |
| # مثال در بخش تست | |
| gr.Examples( | |
| examples=[ | |
| ["John Smith works at Microsoft and earned $50,000 with a 15% bonus."], | |
| ["Sarah Johnson from Google Inc. reported revenues of $2.5 million, representing a 25% increase."], | |
| ["The CEO of Apple, Tim Cook, announced profits of $1.2B with 18.5% growth rate."], | |
| ["Dr. Michael Brown from IBM Corp. received €75,000 salary increase of 12%."], | |
| ["Ms. Lisa Wilson at Amazon reported quarterly results of £500K with 8.7% margin."] | |
| ], | |
| inputs=[text_input], | |
| label="نمونه متنها" | |
| ) | |
| # اجرای برنامه | |
| if __name__ == "__main__": | |
| demo.launch() |