blueradiance commited on
Commit
96f9f46
·
verified ·
1 Parent(s): 08bbb05

Upload 4 files

Browse files
Files changed (4) hide show
  1. LICENSE +1 -5
  2. README.md +3 -3
  3. app.py +59 -17
  4. requirements.txt +1 -1
LICENSE CHANGED
@@ -1,5 +1 @@
1
- MIT License (내부용)
2
- 본 앱은 개인 및 비영리 목적으로 자유롭게 사용할 수 있으며,
3
- 치환 결과에 대한 법적 책임은 사용자에게 있습니다.
4
- 임의 수정 후 재배포를 금지합니다.
5
- 출처 명시: blueradiance / masking-app
 
1
+ 비영리/개인 내부용. 무단 수정 및 재배포 금지. 출처 명시 필수 (blueradiance / masking-app)
 
 
 
 
README.md CHANGED
@@ -1,8 +1,8 @@
1
  ---
2
- title: 민감정보 마스킹기
3
  emoji: 🛡️
4
- colorFrom: indigo
5
- colorTo: blue
6
  sdk: gradio
7
  sdk_version: 4.16.0
8
  app_file: app.py
 
1
  ---
2
+ title: 마스킹 앱 완전체
3
  emoji: 🛡️
4
+ colorFrom: blue
5
+ colorTo: indigo
6
  sdk: gradio
7
  sdk_version: 4.16.0
8
  app_file: app.py
app.py CHANGED
@@ -49,11 +49,41 @@ def refactored_mask_names(original_text, names, start_counter=100):
49
  counter += 1
50
  return masked, mapping
51
 
52
- def sanitize_sensitive_info(text, keyword_string):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  keywords = [k.strip() for k in keyword_string.split(",") if k.strip()]
54
  for kw in keywords:
55
  pattern = rf"\b{re.escape(kw)}\b"
56
- text = re.sub(pattern, "우리기관", text, flags=re.IGNORECASE)
57
  text = re.sub(r"(\d{3})-(\d{4})-(\d{4})", r"\1-****-\3", text)
58
  text = re.sub(r"(\d{4})년 (\d{1,2})월 (\d{1,2})일", r"19**년 \2월 *일", text)
59
  text = re.sub(r"(\d{1,3})번지", r"***번지", text)
@@ -74,24 +104,36 @@ def final_name_remask_exact_only(text, mapping_dict):
74
  text = re.sub(pattern, tag, text)
75
  return text
76
 
77
- def full_pipeline(text, keywords):
78
  names = extract_names(text)
79
  masked, mapping = refactored_mask_names(text, names)
80
- sanitized = sanitize_sensitive_info(masked, keywords)
81
  sanitized = final_name_remask_exact_only(sanitized, mapping)
82
  mapping_table = "\n".join([f"{k} → {v}" for k, v in mapping.items()])
83
  return sanitized, mapping_table
84
 
85
- gr.Interface(
86
- fn=full_pipeline,
87
- inputs=[
88
- gr.Textbox(lines=15, label="📥 원본 텍스트 입력"),
89
- gr.Textbox(lines=1, label="기관 키워드 (쉼표로 구분)", value="굿네이버스, good neighbors, gn, 사회복지법인 굿네이버스")
90
- ],
91
- outputs=[
92
- gr.Textbox(lines=15, label="🔐 마스킹된 텍스트"),
93
- gr.Textbox(lines=10, label="🏷️ 이름 태그 매핑 (NXXX 이름)")
94
- ],
95
- title="🛡️ 민감정보 마스킹 [땡땡이 마스킹] : 이름 + 민감정보 + 초/중/고 마스킹기 (초성 기반)",
96
- description=""
97
- ).launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  counter += 1
50
  return masked, mapping
51
 
52
+ def to_chosung(text):
53
+ CHOSUNG_LIST = [chr(i) for i in range(0x1100, 0x1113)]
54
+ result = ""
55
+ for ch in text:
56
+ if '가' <= ch <= '힣':
57
+ code = ord(ch) - ord('가')
58
+ cho = code // 588
59
+ result += CHOSUNG_LIST[cho]
60
+ else:
61
+ result += ch
62
+ return result
63
+
64
+ def mask_school_names(text):
65
+ school_patterns = [
66
+ (r"(\b[가-힣]{2,20})(초등학교|중학교|고등학교)", True),
67
+ (r"(\b[가-힣]{2,20})\s(초등학교|중학교|고등학교)", False),
68
+ ]
69
+ for pattern, attach in school_patterns:
70
+ text = re.sub(pattern, lambda m: to_chosung(m.group(1)) + (" " if not attach else "") + m.group(2), text)
71
+ return text
72
+
73
+ def mask_department(text):
74
+ text = re.sub(r"([가-힣]{2,20}학과)", lambda m: to_chosung(m.group(1)[:-2]) + "학과", text)
75
+ text = re.sub(r"([가-힣]{2,20})과", lambda m: to_chosung(m.group(1)) + "과" if not re.search(r"정신|의무|외래", m.group(1)) else m.group(0), text)
76
+ return text
77
+
78
+ def sanitize_sensitive_info(text, keyword_string, replace_word):
79
+ text = mask_school_names(text)
80
+ text = mask_department(text)
81
+ text = re.sub(r"(\d)학년\s?(\d)반", r"*학년 *반", text)
82
+
83
  keywords = [k.strip() for k in keyword_string.split(",") if k.strip()]
84
  for kw in keywords:
85
  pattern = rf"\b{re.escape(kw)}\b"
86
+ text = re.sub(pattern, replace_word, text, flags=re.IGNORECASE)
87
  text = re.sub(r"(\d{3})-(\d{4})-(\d{4})", r"\1-****-\3", text)
88
  text = re.sub(r"(\d{4})년 (\d{1,2})월 (\d{1,2})일", r"19**년 \2월 *일", text)
89
  text = re.sub(r"(\d{1,3})번지", r"***번지", text)
 
104
  text = re.sub(pattern, tag, text)
105
  return text
106
 
107
+ def apply_masking(text, keywords, replace_word):
108
  names = extract_names(text)
109
  masked, mapping = refactored_mask_names(text, names)
110
+ sanitized = sanitize_sensitive_info(masked, keywords, replace_word)
111
  sanitized = final_name_remask_exact_only(sanitized, mapping)
112
  mapping_table = "\n".join([f"{k} → {v}" for k, v in mapping.items()])
113
  return sanitized, mapping_table
114
 
115
+ def remask_with_mapping(text, mapping_string):
116
+ mapping = {}
117
+ for line in mapping_string.strip().split("\n"):
118
+ if "→" in line:
119
+ tag, name = line.split("")
120
+ mapping[tag.strip()] = name.strip()
121
+ for tag, name in mapping.items():
122
+ pattern = rf'(?<![\w가-힣]){re.escape(name)}(?![\w가-힣])'
123
+ text = re.sub(pattern, tag, text)
124
+ return text
125
+
126
+ with gr.Blocks() as demo:
127
+ gr.Markdown("🛡️ 민감정보 마스킹 [땡땡이 마스킹] : 이름 + 민감정보 + 초/중/고 마스킹기 (초성 기반)")
128
+ input_text = gr.Textbox(lines=15, label="📥 원본 텍스트 입력")
129
+ keyword_input = gr.Textbox(lines=1, label="기관 키워드 (쉼표로 구분)", value="굿네이버스, good neighbors, gn, 사회복지법인 굿네이버스")
130
+ replace_input = gr.Textbox(lines=1, label="치환할 텍스트", value="우리기관")
131
+ run_button = gr.Button("🚀 마스킹 실행")
132
+ masked_output = gr.Textbox(lines=15, label="🔐 마스킹된 텍스트")
133
+ mapping_output = gr.Textbox(lines=10, label="🏷️ 이름 태그 매핑 (수정 가능)", interactive=True)
134
+ reapply_button = gr.Button("🔁 매핑 재반영")
135
+
136
+ run_button.click(fn=apply_masking, inputs=[input_text, keyword_input, replace_input], outputs=[masked_output, mapping_output])
137
+ reapply_button.click(fn=remask_with_mapping, inputs=[masked_output, mapping_output], outputs=masked_output)
138
+
139
+ demo.launch()
requirements.txt CHANGED
@@ -1,3 +1,3 @@
1
- gradio>=4.16.0
2
  torch
3
  transformers
 
1
+ gradio
2
  torch
3
  transformers