Spaces:
Sleeping
Sleeping
| # File: anonymizer.py | |
| import streamlit as st | |
| from transformers import pipeline | |
| import re | |
| # ใช้ @st.cache_resource เพื่อให้แน่ใจว่าโมเดลจะถูกโหลดแค่ครั้งเดียว | |
| # ซึ่งจะช่วยให้แอปทำงานเร็วขึ้นมากหลังจากการรันครั้งแรก | |
| def load_ner_model(): | |
| """Loads the NER pipeline model and caches it.""" | |
| print("Loading NER model for the first time...") | |
| try: | |
| # เพิ่ม argument token เข้าไป | |
| model = pipeline( | |
| "token-classification", | |
| model="loolootech/no-name-ner-th", | |
| device=-1, | |
| token=st.secrets.get("HUGGING_FACE_TOKEN") # ดึง Token จาก secrets | |
| ) | |
| print("NER model loaded successfully.") | |
| return model | |
| except Exception as e: | |
| st.error(f"Failed to load NER model: {e}") | |
| return None | |
| # กำหนดประเภทของข้อมูลที่จะถูกแทนที่ และ Token ที่จะใช้แทน | |
| ENTITY_TO_ANONYMIZED_TOKEN_MAP = { | |
| "PERSON": "[PERSON]", | |
| "PHONE": "[PHONE]", | |
| "EMAIL": "[EMAIL]", | |
| "ADDRESS": "[LOCATION]", | |
| "DATE": "[DATE]", | |
| "NATIONAL_ID": "[NATIONAL_ID]", | |
| "HOSPITAL_IDS": "[HOSPITAL_IDS]", | |
| "HN": "[HOSPITAL_NUMBER]", | |
| "ORGANIZATION": "[ORGANIZATION]", | |
| "LOCATION": "[LOCATION]", | |
| "URL": "[URL]", | |
| } | |
| def anonymize_text(original_text: str, ner_model): | |
| """ | |
| ใช้โมเดล NER และ RegEx เพื่อปกปิดข้อมูลส่วนบุคคลในข้อความ | |
| """ | |
| if not isinstance(original_text, str) or not original_text.strip(): | |
| return original_text | |
| # ========================================================== | |
| # ✨ ขั้นตอนที่ 1 (เพิ่มใหม่): ใช้ RegEx ค้นหาและแทนที่ HN ก่อน ✨ | |
| # ========================================================== | |
| # รูปแบบ: ค้นหา HN หรือ hn, ตามด้วยเว้นวรรคหรือไม่ก็ได้ ( \s? ), ตามด้วยตัวเลข 1 ตัวขึ้นไป ( \d+ ) | |
| hn_pattern = r'[Hh][Nn]([\s.]?\d+|\s[a-zA-Z]{2}\d+)' | |
| text_after_regex = re.sub(hn_pattern, ENTITY_TO_ANONYMIZED_TOKEN_MAP["HN"], original_text) | |
| # ถ้าไม่มี ner_model ก็ให้คืนค่าหลังจากทำ RegEx ไปเลย | |
| if not ner_model: | |
| return text_after_regex | |
| try: | |
| # ========================================================== | |
| # ✨ ขั้นตอนที่ 2 (ของเดิม): ทำ NER กับข้อความที่ผ่าน RegEx มาแล้ว ✨ | |
| # ========================================================== | |
| ner_results = ner_model(text_after_regex) | |
| if not ner_results: | |
| return text_after_regex | |
| # 2. จัดการ Entity ที่ต่อเนื่องกัน | |
| combined_entities = [] | |
| for entity in ner_results: | |
| entity_name = re.sub(r'^[BI]-', '', entity['entity']) | |
| entity['entity'] = entity_name | |
| if (combined_entities and | |
| combined_entities[-1]['entity'] == entity_name and | |
| entity['start'] == combined_entities[-1]['end']): | |
| combined_entities[-1]['word'] += entity['word'] | |
| combined_entities[-1]['end'] = entity['end'] | |
| elif (combined_entities and | |
| combined_entities[-1]['entity'] == entity_name and | |
| entity['start'] == combined_entities[-1]['end'] + 1 and | |
| text_after_regex[entity['start'] - 1].isspace()): | |
| combined_entities[-1]['word'] += " " + entity['word'] | |
| combined_entities[-1]['end'] = entity['end'] | |
| else: | |
| combined_entities.append(entity) | |
| # 3. คัดกรองเฉพาะ entity ที่เราต้องการปกปิด | |
| entities_to_anonymize = [ | |
| e for e in combined_entities if e['entity'] in ENTITY_TO_ANONYMIZED_TOKEN_MAP | |
| ] | |
| # 4. เรียงลำดับจากท้ายมาหน้า | |
| entities_to_anonymize.sort(key=lambda x: x['start'], reverse=True) | |
| # 5. แทนที่ข้อความ | |
| anonymized_text = text_after_regex | |
| for entity in entities_to_anonymize: | |
| start, end = entity['start'], entity['end'] | |
| token = ENTITY_TO_ANONYMIZED_TOKEN_MAP.get(entity['entity']) | |
| if token: | |
| anonymized_text = anonymized_text[:start] + token + anonymized_text[end:] | |
| return anonymized_text | |
| except Exception: | |
| # หากเกิดข้อผิดพลาดใดๆ ให้คืนค่าข้อความที่ผ่าน RegEx แล้วไปก่อน | |
| return text_after_regex |