petrsovadina commited on
Commit
3d379fe
1 Parent(s): f7e1e6e

Update presidio_streamlit.py

Browse files
Files changed (1) hide show
  1. presidio_streamlit.py +228 -65
presidio_streamlit.py CHANGED
@@ -3,8 +3,10 @@ import logging
3
  import os
4
  import traceback
5
 
 
6
  import pandas as pd
7
  import streamlit as st
 
8
  from annotated_text import annotated_text
9
  from streamlit_tags import st_tags
10
 
@@ -19,7 +21,7 @@ from presidio_helpers import (
19
  )
20
 
21
  st.set_page_config(
22
- page_title="Anonymizace českých textů",
23
  layout="wide",
24
  initial_sidebar_state="expanded",
25
  menu_items={
@@ -27,55 +29,153 @@ st.set_page_config(
27
  },
28
  )
29
 
 
30
  logger = logging.getLogger("presidio-streamlit")
31
 
 
 
32
  # Sidebar
33
- st.sidebar.header("Anonymizace osobních údajů v českých textech")
 
 
 
 
34
 
35
  model_help_text = """
36
- Vyberte model pro rozpoznávání pojmenovaných entit (NER) pro detekci osobních údajů.
 
 
37
  """
 
38
 
39
  model_list = [
40
- "iiiorg/piiranha-v1-detect-personal-information",
41
  "spaCy/cs_core_news_sm",
 
 
42
  ]
43
-
 
 
44
  st_model = st.sidebar.selectbox(
45
  "NER model",
46
  model_list,
47
- index=0,
48
  help=model_help_text,
49
  )
50
 
 
51
  st_model_package = st_model.split("/")[0]
52
- st_model = "/".join(st_model.split("/")[1:])
53
 
54
- analyzer_params = (st_model_package, st_model, None, None)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  logger.debug(f"analyzer_params: {analyzer_params}")
56
 
57
  st_operator = st.sidebar.selectbox(
58
- "Metoda anonymizace",
59
- ["nahrazení", "maskování", "zvýraznění"],
60
- index=0,
61
  help="""
62
- Vyberte způsob anonymizace textu po identifikaci osobních údajů.\n
63
- - Nahrazení: Nahradí osobní údaj obecným označením, např. <OSOBA>\n
64
- - Maskování: Nahradí část osobního údaje hvězdičkami\n
65
- - Zvýraznění: Zvýrazní osobní údaje v původním textu
66
- """,
 
 
 
 
67
  )
68
-
69
  st_mask_char = "*"
70
- st_number_of_chars = 4
 
 
 
71
 
72
- if st_operator == "maskování":
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  st_number_of_chars = st.sidebar.number_input(
74
- "Počet znaků k maskování", value=st_number_of_chars, min_value=0, max_value=100
75
  )
76
  st_mask_char = st.sidebar.text_input(
77
  "Znak pro maskování", value=st_mask_char, max_chars=1
78
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
 
80
  st_threshold = st.sidebar.slider(
81
  label="Práh přijetí",
@@ -85,68 +185,145 @@ st_threshold = st.sidebar.slider(
85
  help="Definujte práh pro přijetí detekce jako osobní údaj.",
86
  )
87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  # Hlavní panel
89
- st.title("Anonymizace českých textů")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
 
91
- # Načtení ukázkového textu
92
  with open("demo_text.txt", "r", encoding="utf-8") as f:
93
  demo_text = f.read()
94
 
95
- # Vytvoření dvou sloupců pro vstup a výstup
96
  col1, col2 = st.columns(2)
97
 
98
- # Vstup
99
- col1.subheader("Vstupní text")
100
  st_text = col1.text_area(
101
  label="Zadejte text", value=demo_text, height=400, key="text_input"
102
  )
103
 
104
  try:
105
  # Výběr entit
106
- st_entities_expander = st.sidebar.expander("Vyberte entity k detekci")
107
  st_entities = st_entities_expander.multiselect(
108
  label="Které entity hledat?",
109
  options=get_supported_entities(*analyzer_params),
110
  default=list(get_supported_entities(*analyzer_params)),
111
- help="Omezte seznam detekovaných osobních údajů.",
 
 
112
  )
113
 
114
- # Inicializace analyzátoru
115
  analyzer_load_state = st.info("Spouštění Presidio analyzátoru...")
116
  analyzer = analyzer_engine(*analyzer_params)
117
  analyzer_load_state.empty()
118
 
119
- # Analýza textu
120
  st_analyze_results = analyze(
121
  *analyzer_params,
122
  text=st_text,
123
  entities=st_entities,
124
  language="cs",
125
  score_threshold=st_threshold,
126
- return_decision_process=False,
127
- allow_list=[],
128
- deny_list=[],
129
- )
130
-
131
- # Výstup
132
- col2.subheader("Výstup")
133
- if st_operator != "zvýraznění":
134
- st_anonymize_results = anonymize(
135
- text=st_text,
136
- operator=st_operator,
137
- mask_char=st_mask_char,
138
- number_of_chars=st_number_of_chars,
139
- analyze_results=st_analyze_results,
140
- )
141
- col2.text_area(
142
- label="Anonymizovaný text", value=st_anonymize_results.text, height=400
143
- )
 
 
 
 
 
 
 
 
 
 
 
144
  else:
 
145
  annotated_tokens = annotate(text=st_text, analyze_results=st_analyze_results)
146
  annotated_text(*annotated_tokens)
147
 
148
- # Tabulka s výsledky
149
- st.subheader("Nalezené osobní údaje")
 
 
 
 
150
  if st_analyze_results:
151
  df = pd.DataFrame.from_records([r.to_dict() for r in st_analyze_results])
152
  df["text"] = [st_text[res.start : res.end] for res in st_analyze_results]
@@ -161,19 +338,5 @@ try:
161
  },
162
  axis=1,
163
  )
164
- st.dataframe(df_subset.reset_index(drop=True), use_container_width=True)
165
- else:
166
- st.text("Žádné osobní údaje nebyly nalezeny.")
167
-
168
- except Exception as e:
169
- print(e)
170
- traceback.print_exc()
171
- st.error(f"Došlo k chybě: {str(e)}")
172
-
173
- # Informace o aplikaci
174
- st.sidebar.markdown("---")
175
- st.sidebar.subheader("O aplikaci")
176
- st.sidebar.info(
177
- "Tato aplikace anonymizuje osobní údaje v českých textech. "
178
- "Využívá Microsoft Presidio a pokročilé NLP techniky pro detekci a anonymizaci PII."
179
- )
 
3
  import os
4
  import traceback
5
 
6
+ import dotenv
7
  import pandas as pd
8
  import streamlit as st
9
+ import streamlit.components.v1 as components
10
  from annotated_text import annotated_text
11
  from streamlit_tags import st_tags
12
 
 
21
  )
22
 
23
  st.set_page_config(
24
+ page_title="Presidio demo pro české texty",
25
  layout="wide",
26
  initial_sidebar_state="expanded",
27
  menu_items={
 
29
  },
30
  )
31
 
32
+ dotenv.load_dotenv()
33
  logger = logging.getLogger("presidio-streamlit")
34
 
35
+ allow_other_models = os.getenv("ALLOW_OTHER_MODELS", False)
36
+
37
  # Sidebar
38
+ st.sidebar.header(
39
+ """
40
+ Anonymizace osobních údajů v českých textech s [Microsoft Presidio](https://microsoft.github.io/presidio/)
41
+ """
42
+ )
43
 
44
  model_help_text = """
45
+ Vyberte model pro rozpoznávání pojmenovaných entit (NER) pro detekci osobních údajů v českých textech.
46
+ Presidio podporuje různé NER balíčky, jako jsou spaCy, Huggingface, Stanza a Flair,
47
+ stejně jako služby jako Azure Text Analytics PII.
48
  """
49
+ st_ta_key = st_ta_endpoint = ""
50
 
51
  model_list = [
 
52
  "spaCy/cs_core_news_sm",
53
+ "iiiorg/piiranha-v1-detect-personal-information",
54
+ "FacebookAI/xlm-roberta-large-finetuned-conll03-english",
55
  ]
56
+ if not allow_other_models:
57
+ model_list.pop()
58
+ # Výběr modelu
59
  st_model = st.sidebar.selectbox(
60
  "NER model",
61
  model_list,
62
+ index=1,
63
  help=model_help_text,
64
  )
65
 
66
+ # Extrakce balíčku modelu
67
  st_model_package = st_model.split("/")[0]
 
68
 
69
+ # Odstranění prefixu balíčku (pokud je potřeba)
70
+ st_model = (
71
+ st_model
72
+ if st_model_package.lower() not in ("spacy", "piiiranha")
73
+ else "/".join(st_model.split("/")[1:])
74
+ )
75
+
76
+ if st_model == "Other":
77
+ st_model_package = st.sidebar.selectbox(
78
+ "NER model OSS balíček", options=["spacy", "piiiranha"]
79
+ )
80
+ st_model = st.sidebar.text_input(f"Název NER modelu", value="")
81
+
82
+ st.sidebar.warning("Poznámka: Stažení modelů může chvíli trvat.")
83
+
84
+ analyzer_params = (st_model_package, st_model, st_ta_key, st_ta_endpoint)
85
  logger.debug(f"analyzer_params: {analyzer_params}")
86
 
87
  st_operator = st.sidebar.selectbox(
88
+ "Přístup k anonymizaci",
89
+ ["redact", "replace", "synthesize", "highlight", "mask", "hash", "encrypt"],
90
+ index=1,
91
  help="""
92
+ Vyberte způsob úpravy textu po identifikaci osobních údajů.\n
93
+ - Redact: Kompletně odstranit osobní údaj\n
94
+ - Replace: Nahradit osobní údaj konstantou, např. <OSOBA>\n
95
+ - Synthesize: Nahradit falešnými hodnotami (vyžaduje OpenAI klíč)\n
96
+ - Highlight: Zobrazí původní text se zvýrazněnými osobními údaji\n
97
+ - Mask: Nahradí požadovaný počet znaků hvězdičkou (nebo jiným znakem)\n
98
+ - Hash: Nahradí hashem osobního údaje\n
99
+ - Encrypt: Nahradí AES šifrováním osobního údaje, umožňující reverzní proces
100
+ """,
101
  )
 
102
  st_mask_char = "*"
103
+ st_number_of_chars = 15
104
+ st_encrypt_key = "WmZq4t7w!z%C&F)J"
105
+
106
+ open_ai_params = None
107
 
108
+ logger.debug(f"st_operator: {st_operator}")
109
+
110
+ def set_up_openai_synthesis():
111
+ """Nastavení OpenAI API klíče a modelu pro syntézu textu."""
112
+
113
+ if os.getenv("OPENAI_TYPE", default="openai") == "Azure":
114
+ openai_api_type = "azure"
115
+ st_openai_api_base = st.sidebar.text_input(
116
+ "Azure OpenAI base URL",
117
+ value=os.getenv("AZURE_OPENAI_ENDPOINT", default=""),
118
+ )
119
+ openai_key = os.getenv("AZURE_OPENAI_KEY", default="")
120
+ st_deployment_id = st.sidebar.text_input(
121
+ "Název nasazení", value=os.getenv("AZURE_OPENAI_DEPLOYMENT", default="")
122
+ )
123
+ st_openai_version = st.sidebar.text_input(
124
+ "OpenAI verze",
125
+ value=os.getenv("OPENAI_API_VERSION", default="2023-05-15"),
126
+ )
127
+ else:
128
+ openai_api_type = "openai"
129
+ st_openai_version = st_openai_api_base = None
130
+ st_deployment_id = ""
131
+ openai_key = os.getenv("OPENAI_KEY", default="")
132
+ st_openai_key = st.sidebar.text_input(
133
+ "OPENAI_KEY",
134
+ value=openai_key,
135
+ help="Více informací na https://help.openai.com/en/articles/4936850-where-do-i-find-my-secret-api-key",
136
+ type="password",
137
+ )
138
+ st_openai_model = st.sidebar.text_input(
139
+ "OpenAI model pro syntézu textu",
140
+ value=os.getenv("OPENAI_MODEL", default="gpt-3.5-turbo-instruct"),
141
+ help="Více informací zde: https://platform.openai.com/docs/models/",
142
+ )
143
+ return (
144
+ openai_api_type,
145
+ st_openai_api_base,
146
+ st_deployment_id,
147
+ st_openai_version,
148
+ st_openai_key,
149
+ st_openai_model,
150
+ )
151
+
152
+ if st_operator == "mask":
153
  st_number_of_chars = st.sidebar.number_input(
154
+ "počet znaků", value=st_number_of_chars, min_value=0, max_value=100
155
  )
156
  st_mask_char = st.sidebar.text_input(
157
  "Znak pro maskování", value=st_mask_char, max_chars=1
158
  )
159
+ elif st_operator == "encrypt":
160
+ st_encrypt_key = st.sidebar.text_input("AES klíč", value=st_encrypt_key)
161
+ elif st_operator == "synthesize":
162
+ (
163
+ openai_api_type,
164
+ st_openai_api_base,
165
+ st_deployment_id,
166
+ st_openai_version,
167
+ st_openai_key,
168
+ st_openai_model,
169
+ ) = set_up_openai_synthesis()
170
+
171
+ open_ai_params = OpenAIParams(
172
+ openai_key=st_openai_key,
173
+ model=st_openai_model,
174
+ api_base=st_openai_api_base,
175
+ deployment_id=st_deployment_id,
176
+ api_version=st_openai_version,
177
+ api_type=openai_api_type,
178
+ )
179
 
180
  st_threshold = st.sidebar.slider(
181
  label="Práh přijetí",
 
185
  help="Definujte práh pro přijetí detekce jako osobní údaj.",
186
  )
187
 
188
+ st_return_decision_process = st.sidebar.checkbox(
189
+ "Přidat vysvětlení analýzy k nálezům",
190
+ value=False,
191
+ help="Přidá rozhodovací proces do výstupní tabulky. "
192
+ "Více informací najdete zde: https://microsoft.github.io/presidio/analyzer/decision_process/",
193
+ )
194
+
195
+ # Povolené a zakázané seznamy
196
+ st_deny_allow_expander = st.sidebar.expander(
197
+ "Povolené a zakázané seznamy",
198
+ expanded=False,
199
+ )
200
+
201
+ with st_deny_allow_expander:
202
+ st_allow_list = st_tags(
203
+ label="Přidat slova do povoleného seznamu", text="Zadejte slovo a stiskněte enter."
204
+ )
205
+ st.caption(
206
+ "Povolené seznamy obsahují slova, která nejsou považována za osobní údaje, ale jsou jako takové detekována."
207
+ )
208
+
209
+ st_deny_list = st_tags(
210
+ label="Přidat slova do zakázaného seznamu", text="Zadejte slovo a stiskněte enter."
211
+ )
212
+ st.caption(
213
+ "Zakázané seznamy obsahují slova, která jsou považována za osobní údaje, ale nejsou jako takové detekována."
214
+ )
215
+
216
  # Hlavní panel
217
+ with st.expander("O této ukázce", expanded=False):
218
+ st.info(
219
+ """Presidio je open source přizpůsobitelný framework pro detekci a anonymizaci osobních údajů.
220
+ \n\n[Kód](https://aka.ms/presidio) |
221
+ [Tutoriál](https://microsoft.github.io/presidio/tutorial/) |
222
+ [Instalace](https://microsoft.github.io/presidio/installation/) |
223
+ [FAQ](https://microsoft.github.io/presidio/faq/) |
224
+ [Zpětná vazba](https://forms.office.com/r/9ufyYjfDaY) |"""
225
+ )
226
+
227
+ st.info(
228
+ """
229
+ Použijte tuto ukázku k:
230
+ - Experimentování s různými hotovými modely a NLP balíčky.
231
+ - Prozkoumání různých možností anonymizace, včetně redakce, maskování, šifrování a dalších.
232
+ - Generování syntetického textu s Microsoft Presidio a OpenAI.
233
+ - Konfiguraci povolených a zakázaných seznamů.
234
+
235
+ Tato ukázková webová stránka ukazuje některé z možností Presidio.
236
+ [Navštivte naši webovou stránku](https://microsoft.github.io/presidio) pro více informací,
237
+ ukázek a možností nasazení.
238
+ """
239
+ )
240
+
241
+ st.markdown(
242
+ "[![Pypi Downloads](https://img.shields.io/pypi/dm/presidio-analyzer.svg)](https://img.shields.io/pypi/dm/presidio-analyzer.svg)" # noqa
243
+ "[![MIT license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT)"
244
+ "![GitHub Repo stars](https://img.shields.io/github/stars/microsoft/presidio?style=social)"
245
+ )
246
+
247
+ analyzer_load_state = st.info("Spouštění Presidio analyzátoru...")
248
+
249
+ analyzer_load_state.empty()
250
 
251
+ # Načtení výchozího textu
252
  with open("demo_text.txt", "r", encoding="utf-8") as f:
253
  demo_text = f.read()
254
 
255
+ # Vytvoření dvou sloupců pro před a po
256
  col1, col2 = st.columns(2)
257
 
258
+ # Před:
259
+ col1.subheader("Vstup")
260
  st_text = col1.text_area(
261
  label="Zadejte text", value=demo_text, height=400, key="text_input"
262
  )
263
 
264
  try:
265
  # Výběr entit
266
+ st_entities_expander = st.sidebar.expander("Vyberte entity k vyhledání")
267
  st_entities = st_entities_expander.multiselect(
268
  label="Které entity hledat?",
269
  options=get_supported_entities(*analyzer_params),
270
  default=list(get_supported_entities(*analyzer_params)),
271
+ help="Omezte seznam detekovaných osobních údajů. "
272
+ "Tento seznam je dynamický a závisí na NER modelu a registrovaných rozpoznávačích. "
273
+ "Více informací najdete zde: https://microsoft.github.io/presidio/analyzer/adding_recognizers/",
274
  )
275
 
276
+ # Před
277
  analyzer_load_state = st.info("Spouštění Presidio analyzátoru...")
278
  analyzer = analyzer_engine(*analyzer_params)
279
  analyzer_load_state.empty()
280
 
 
281
  st_analyze_results = analyze(
282
  *analyzer_params,
283
  text=st_text,
284
  entities=st_entities,
285
  language="cs",
286
  score_threshold=st_threshold,
287
+ return_decision_process=st_return_decision_process,
288
+ allow_list=st_allow_list,
289
+ deny_list=st_deny_list,
290
+ )
291
+
292
+ # Po
293
+ if st_operator not in ("highlight", "synthesize"):
294
+ with col2:
295
+ st.subheader(f"Výstup")
296
+ st_anonymize_results = anonymize(
297
+ text=st_text,
298
+ operator=st_operator,
299
+ mask_char=st_mask_char,
300
+ number_of_chars=st_number_of_chars,
301
+ encrypt_key=st_encrypt_key,
302
+ analyze_results=st_analyze_results,
303
+ )
304
+ st.text_area(
305
+ label="Anonymizováno", value=st_anonymize_results.text, height=400
306
+ )
307
+ elif st_operator == "synthesize":
308
+ with col2:
309
+ st.subheader(f"OpenAI Generovaný výstup")
310
+ fake_data = create_fake_data(
311
+ st_text,
312
+ st_analyze_results,
313
+ open_ai_params,
314
+ )
315
+ st.text_area(label="Syntetická data", value=fake_data, height=400)
316
  else:
317
+ st.subheader("Zvýrazněno")
318
  annotated_tokens = annotate(text=st_text, analyze_results=st_analyze_results)
319
  annotated_text(*annotated_tokens)
320
 
321
+ # tabulka výsledků
322
+ st.subheader(
323
+ "Nálezy"
324
+ if not st_return_decision_process
325
+ else "Nálezy s rozhodovacími faktory"
326
+ )
327
  if st_analyze_results:
328
  df = pd.DataFrame.from_records([r.to_dict() for r in st_analyze_results])
329
  df["text"] = [st_text[res.start : res.end] for res in st_analyze_results]
 
338
  },
339
  axis=1,
340
  )
341
+ df_subset["Text"] = [st_text[res.start : res.end] for res in st_analyze_results]
342
+ if st