File size: 11,740 Bytes
f7e1e6e
3477655
547518c
3477655
384d9d6
3d379fe
384d9d6
 
3d379fe
384d9d6
3477655
384d9d6
3477655
d6241cc
 
 
 
 
 
 
384d9d6
 
3477655
3d379fe
3477655
 
 
 
 
 
 
3d379fe
3477655
 
3d379fe
 
384d9d6
3d379fe
 
 
 
 
384d9d6
3477655
3d379fe
 
 
3477655
3d379fe
384d9d6
3477655
f7e1e6e
3d379fe
 
3477655
3d379fe
 
 
384d9d6
f7e1e6e
3477655
3d379fe
3477655
384d9d6
3477655
3d379fe
3477655
 
3d379fe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3477655
384d9d6
 
3d379fe
 
 
547518c
3d379fe
 
 
 
 
 
 
 
 
384d9d6
f7e1e6e
3d379fe
 
 
 
57594ac
3d379fe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57594ac
3d379fe
57594ac
 
f7e1e6e
3477655
3d379fe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3477655
384d9d6
f7e1e6e
547518c
 
 
f7e1e6e
384d9d6
 
3d379fe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f7e1e6e
3d379fe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3477655
3d379fe
f7e1e6e
 
3477655
3d379fe
384d9d6
 
3d379fe
 
384d9d6
f7e1e6e
384d9d6
 
3477655
f7e1e6e
3d379fe
3477655
f7e1e6e
3477655
 
3d379fe
 
 
d6241cc
384d9d6
3d379fe
f7e1e6e
3477655
 
384d9d6
3477655
 
 
 
f7e1e6e
3477655
3d379fe
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3477655
3d379fe
3477655
 
384d9d6
3d379fe
 
 
 
 
 
3477655
 
 
384d9d6
3477655
 
f7e1e6e
3477655
f7e1e6e
 
 
3477655
 
384d9d6
3d379fe
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
"""Streamlit app pro anonymizaci českých textů s využitím Presidio."""
import logging
import os
import traceback

import dotenv
import pandas as pd
import streamlit as st
import streamlit.components.v1 as components
from annotated_text import annotated_text
from streamlit_tags import st_tags

from openai_fake_data_generator import OpenAIParams
from presidio_helpers import (
    get_supported_entities,
    analyze,
    anonymize,
    annotate,
    create_fake_data,
    analyzer_engine,
)

st.set_page_config(
    page_title="Presidio demo pro české texty",
    layout="wide",
    initial_sidebar_state="expanded",
    menu_items={
        "About": "https://microsoft.github.io/presidio/",
    },
)

dotenv.load_dotenv()
logger = logging.getLogger("presidio-streamlit")

allow_other_models = os.getenv("ALLOW_OTHER_MODELS", False)

# Sidebar
st.sidebar.header(
    """
Anonymizace osobních údajů v českých textech s [Microsoft Presidio](https://microsoft.github.io/presidio/)
"""
)

model_help_text = """
    Vyberte model pro rozpoznávání pojmenovaných entit (NER) pro detekci osobních údajů v českých textech.
    Presidio podporuje různé NER balíčky, jako jsou spaCy, Huggingface, Stanza a Flair,
    stejně jako služby jako Azure Text Analytics PII.
    """
st_ta_key = st_ta_endpoint = ""

model_list = [
    "spaCy/cs_core_news_sm",
    "iiiorg/piiranha-v1-detect-personal-information",
    "FacebookAI/xlm-roberta-large-finetuned-conll03-english",
]
if not allow_other_models:
    model_list.pop()
# Výběr modelu
st_model = st.sidebar.selectbox(
    "NER model",
    model_list,
    index=1,
    help=model_help_text,
)

# Extrakce balíčku modelu
st_model_package = st_model.split("/")[0]

# Odstranění prefixu balíčku (pokud je potřeba)
st_model = (
    st_model
    if st_model_package.lower() not in ("spacy", "piiiranha")
    else "/".join(st_model.split("/")[1:])
)

if st_model == "Other":
    st_model_package = st.sidebar.selectbox(
        "NER model OSS balíček", options=["spacy", "piiiranha"]
    )
    st_model = st.sidebar.text_input(f"Název NER modelu", value="")

st.sidebar.warning("Poznámka: Stažení modelů může chvíli trvat.")

analyzer_params = (st_model_package, st_model, st_ta_key, st_ta_endpoint)
logger.debug(f"analyzer_params: {analyzer_params}")

st_operator = st.sidebar.selectbox(
    "Přístup k anonymizaci",
    ["redact", "replace", "synthesize", "highlight", "mask", "hash", "encrypt"],
    index=1,
    help="""
    Vyberte způsob úpravy textu po identifikaci osobních údajů.\n
    - Redact: Kompletně odstranit osobní údaj\n
    - Replace: Nahradit osobní údaj konstantou, např. <OSOBA>\n
    - Synthesize: Nahradit falešnými hodnotami (vyžaduje OpenAI klíč)\n
    - Highlight: Zobrazí původní text se zvýrazněnými osobními údaji\n
    - Mask: Nahradí požadovaný počet znaků hvězdičkou (nebo jiným znakem)\n
    - Hash: Nahradí hashem osobního údaje\n
    - Encrypt: Nahradí AES šifrováním osobního údaje, umožňující reverzní proces
         """,
)
st_mask_char = "*"
st_number_of_chars = 15
st_encrypt_key = "WmZq4t7w!z%C&F)J"

open_ai_params = None

logger.debug(f"st_operator: {st_operator}")

def set_up_openai_synthesis():
    """Nastavení OpenAI API klíče a modelu pro syntézu textu."""

    if os.getenv("OPENAI_TYPE", default="openai") == "Azure":
        openai_api_type = "azure"
        st_openai_api_base = st.sidebar.text_input(
            "Azure OpenAI base URL",
            value=os.getenv("AZURE_OPENAI_ENDPOINT", default=""),
        )
        openai_key = os.getenv("AZURE_OPENAI_KEY", default="")
        st_deployment_id = st.sidebar.text_input(
            "Název nasazení", value=os.getenv("AZURE_OPENAI_DEPLOYMENT", default="")
        )
        st_openai_version = st.sidebar.text_input(
            "OpenAI verze",
            value=os.getenv("OPENAI_API_VERSION", default="2023-05-15"),
        )
    else:
        openai_api_type = "openai"
        st_openai_version = st_openai_api_base = None
        st_deployment_id = ""
        openai_key = os.getenv("OPENAI_KEY", default="")
    st_openai_key = st.sidebar.text_input(
        "OPENAI_KEY",
        value=openai_key,
        help="Více informací na https://help.openai.com/en/articles/4936850-where-do-i-find-my-secret-api-key",
        type="password",
    )
    st_openai_model = st.sidebar.text_input(
        "OpenAI model pro syntézu textu",
        value=os.getenv("OPENAI_MODEL", default="gpt-3.5-turbo-instruct"),
        help="Více informací zde: https://platform.openai.com/docs/models/",
    )
    return (
        openai_api_type,
        st_openai_api_base,
        st_deployment_id,
        st_openai_version,
        st_openai_key,
        st_openai_model,
    )

if st_operator == "mask":
    st_number_of_chars = st.sidebar.number_input(
        "počet znaků", value=st_number_of_chars, min_value=0, max_value=100
    )
    st_mask_char = st.sidebar.text_input(
        "Znak pro maskování", value=st_mask_char, max_chars=1
    )
elif st_operator == "encrypt":
    st_encrypt_key = st.sidebar.text_input("AES klíč", value=st_encrypt_key)
elif st_operator == "synthesize":
    (
        openai_api_type,
        st_openai_api_base,
        st_deployment_id,
        st_openai_version,
        st_openai_key,
        st_openai_model,
    ) = set_up_openai_synthesis()

    open_ai_params = OpenAIParams(
        openai_key=st_openai_key,
        model=st_openai_model,
        api_base=st_openai_api_base,
        deployment_id=st_deployment_id,
        api_version=st_openai_version,
        api_type=openai_api_type,
    )

st_threshold = st.sidebar.slider(
    label="Práh přijetí",
    min_value=0.0,
    max_value=1.0,
    value=0.35,
    help="Definujte práh pro přijetí detekce jako osobní údaj.",
)

st_return_decision_process = st.sidebar.checkbox(
    "Přidat vysvětlení analýzy k nálezům",
    value=False,
    help="Přidá rozhodovací proces do výstupní tabulky. "
    "Více informací najdete zde: https://microsoft.github.io/presidio/analyzer/decision_process/",
)

# Povolené a zakázané seznamy
st_deny_allow_expander = st.sidebar.expander(
    "Povolené a zakázané seznamy",
    expanded=False,
)

with st_deny_allow_expander:
    st_allow_list = st_tags(
        label="Přidat slova do povoleného seznamu", text="Zadejte slovo a stiskněte enter."
    )
    st.caption(
        "Povolené seznamy obsahují slova, která nejsou považována za osobní údaje, ale jsou jako takové detekována."
    )

    st_deny_list = st_tags(
        label="Přidat slova do zakázaného seznamu", text="Zadejte slovo a stiskněte enter."
    )
    st.caption(
        "Zakázané seznamy obsahují slova, která jsou považována za osobní údaje, ale nejsou jako takové detekována."
    )

# Hlavní panel
with st.expander("O této ukázce", expanded=False):
    st.info(
        """Presidio je open source přizpůsobitelný framework pro detekci a anonymizaci osobních údajů.
        \n\n[Kód](https://aka.ms/presidio) | 
        [Tutoriál](https://microsoft.github.io/presidio/tutorial/) | 
        [Instalace](https://microsoft.github.io/presidio/installation/) | 
        [FAQ](https://microsoft.github.io/presidio/faq/) |
        [Zpětná vazba](https://forms.office.com/r/9ufyYjfDaY) |"""
    )

    st.info(
        """
    Použijte tuto ukázku k:
    - Experimentování s různými hotovými modely a NLP balíčky.
    - Prozkoumání různých možností anonymizace, včetně redakce, maskování, šifrování a dalších.
    - Generování syntetického textu s Microsoft Presidio a OpenAI.
    - Konfiguraci povolených a zakázaných seznamů.
    
    Tato ukázková webová stránka ukazuje některé z možností Presidio.
    [Navštivte naši webovou stránku](https://microsoft.github.io/presidio) pro více informací,
    ukázek a možností nasazení.    
    """
    )

    st.markdown(
        "[![Pypi Downloads](https://img.shields.io/pypi/dm/presidio-analyzer.svg)](https://img.shields.io/pypi/dm/presidio-analyzer.svg)"  # noqa
        "[![MIT license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT)"
        "![GitHub Repo stars](https://img.shields.io/github/stars/microsoft/presidio?style=social)"
    )

analyzer_load_state = st.info("Spouštění Presidio analyzátoru...")

analyzer_load_state.empty()

# Načtení výchozího textu
with open("demo_text.txt", "r", encoding="utf-8") as f:
    demo_text = f.read()

# Vytvoření dvou sloupců pro před a po
col1, col2 = st.columns(2)

# Před:
col1.subheader("Vstup")
st_text = col1.text_area(
    label="Zadejte text", value=demo_text, height=400, key="text_input"
)

try:
    # Výběr entit
    st_entities_expander = st.sidebar.expander("Vyberte entity k vyhledání")
    st_entities = st_entities_expander.multiselect(
        label="Které entity hledat?",
        options=get_supported_entities(*analyzer_params),
        default=list(get_supported_entities(*analyzer_params)),
        help="Omezte seznam detekovaných osobních údajů. "
        "Tento seznam je dynamický a závisí na NER modelu a registrovaných rozpoznávačích. "
        "Více informací najdete zde: https://microsoft.github.io/presidio/analyzer/adding_recognizers/",
    )

    # Před
    analyzer_load_state = st.info("Spouštění Presidio analyzátoru...")
    analyzer = analyzer_engine(*analyzer_params)
    analyzer_load_state.empty()

    st_analyze_results = analyze(
        *analyzer_params,
        text=st_text,
        entities=st_entities,
        language="cs",
        score_threshold=st_threshold,
        return_decision_process=st_return_decision_process,
        allow_list=st_allow_list,
        deny_list=st_deny_list,
    )

    # Po
    if st_operator not in ("highlight", "synthesize"):
        with col2:
            st.subheader(f"Výstup")
            st_anonymize_results = anonymize(
                text=st_text,
                operator=st_operator,
                mask_char=st_mask_char,
                number_of_chars=st_number_of_chars,
                encrypt_key=st_encrypt_key,
                analyze_results=st_analyze_results,
            )
            st.text_area(
                label="Anonymizováno", value=st_anonymize_results.text, height=400
            )
    elif st_operator == "synthesize":
        with col2:
            st.subheader(f"OpenAI Generovaný výstup")
            fake_data = create_fake_data(
                st_text,
                st_analyze_results,
                open_ai_params,
            )
            st.text_area(label="Syntetická data", value=fake_data, height=400)
    else:
        st.subheader("Zvýrazněno")
        annotated_tokens = annotate(text=st_text, analyze_results=st_analyze_results)
        annotated_text(*annotated_tokens)

    # tabulka výsledků
    st.subheader(
        "Nálezy"
        if not st_return_decision_process
        else "Nálezy s rozhodovacími faktory"
    )
    if st_analyze_results:
        df = pd.DataFrame.from_records([r.to_dict() for r in st_analyze_results])
        df["text"] = [st_text[res.start : res.end] for res in st_analyze_results]

        df_subset = df[["entity_type", "text", "start", "end", "score"]].rename(
            {
                "entity_type": "Typ entity",
                "text": "Text",
                "start": "Začátek",
                "end": "Konec",
                "score": "Důvěryhodnost",
            },
            axis=1,
        )
        df_subset["Text"] = [st_text[res.start : res.end] for res in st_analyze_results]
        if st