nlpblogs commited on
Commit
cd28ffa
·
verified ·
1 Parent(s): 2dae987

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +247 -0
app.py ADDED
@@ -0,0 +1,247 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import time
3
+ import pandas as pd
4
+ import io
5
+ from transformers import pipeline
6
+ from streamlit_extras.stylable_container import stylable_container
7
+ import plotly.express as px
8
+ import zipfile
9
+ from PyPDF2 import PdfReader
10
+ import docx
11
+ import os
12
+ from comet_ml import Experiment
13
+ import re
14
+ import numpy as np
15
+
16
+ st.set_page_config(layout="wide", page_title="Named Entity Recognition App")
17
+
18
+
19
+
20
+ # --- Configuration ---
21
+ COMET_API_KEY = os.environ.get("COMET_API_KEY")
22
+ COMET_WORKSPACE = os.environ.get("COMET_WORKSPACE")
23
+ COMET_PROJECT_NAME = os.environ.get("COMET_PROJECT_NAME")
24
+
25
+ comet_initialized = False
26
+ if COMET_API_KEY and COMET_WORKSPACE and COMET_PROJECT_NAME:
27
+ comet_initialized = True
28
+
29
+ # --- Initialize session state ---
30
+ if 'file_upload_attempts' not in st.session_state:
31
+ st.session_state['file_upload_attempts'] = 0
32
+
33
+ max_attempts = 10
34
+
35
+ # --- Helper function for model loading ---
36
+ @st.cache_resource
37
+ def load_ner_model():
38
+ """Loads the pre-trained NER model and caches it."""
39
+ return GLiNER.from_pretrained("urchade/gliner_multi_pii-v1")
40
+
41
+ # --- UI Elements ---
42
+ st.subheader("4-Spanish Named Entity Recognition Web App", divider="rainbow")
43
+ st.link_button("by nlpblogs", "https://nlpblogs.com", type="tertiary")
44
+
45
+ expander = st.expander("**Important notes on the 4-Spanish Named Entity Recognition Web App**")
46
+ expander.write('''
47
+
48
+ **Named Entities:**
49
+ This 4-Spanish Named Entity Recognition Web App predicts four (4) labels (“PER: person”, “LOC: location”, “ORG: organization”, “OTH: other”). Results are presented in an easy-to-read table, visualized in an interactive tree map, pie chart, and bar chart, and are available for download along with a Glossary of tags.
50
+
51
+ **How to Use:**
52
+ Upload your .pdf or .docx file. Then, click the 'Results' button to extract and tag entities in your text data.
53
+
54
+ **Usage Limits:**
55
+ You can request results up to 10 times.
56
+
57
+ **Customization:**
58
+ To change the app's background color to white or black, click the three-dot menu on the right-hand side of your app, go to Settings and then Choose app theme, colors and fonts.
59
+
60
+ **Technical issues:**
61
+ If your connection times out, please refresh the page or reopen the app's URL.
62
+
63
+ For any errors or inquiries, please contact us at [email protected]
64
+
65
+ ''')
66
+
67
+
68
+ with st.sidebar:
69
+ container = st.container(border=True)
70
+ container.write("**Named Entity Recognition (NER)** is the task of extracting and tagging entities in text data. Entities can be persons, organizations, locations, countries, products, events etc.")
71
+ st.subheader("Related NLP Web Apps", divider="rainbow")
72
+ st.link_button("58-Italian Named Entity Recognition Web App", "https://nlpblogs.com/shop/named-entity-recognition-ner/58-italian-named-entity-recognition-web-app/", type = "primary")
73
+
74
+ # --- File Upload ---
75
+ upload_file = st.file_uploader("Upload your file. Accepted file formats include: .pdf, .docx", type=['pdf', 'docx'])
76
+ text = None
77
+ df = None
78
+
79
+ if upload_file is not None:
80
+ file_extension = upload_file.name.split('.')[-1].lower()
81
+ if file_extension == 'pdf':
82
+ try:
83
+ pdf_reader = PdfReader(upload_file)
84
+ text = ""
85
+ for page in pdf_reader.pages:
86
+ text += page.extract_text()
87
+ st.write("File uploaded successfully. Due to security protocols, the file content is hidden.")
88
+ except Exception as e:
89
+ st.error(f"An error occurred while reading PDF: {e}")
90
+ text = None
91
+ elif file_extension == 'docx':
92
+ try:
93
+ doc = docx.Document(upload_file)
94
+ text = "\n".join([para.text for para in doc.paragraphs])
95
+ st.write("File uploaded successfully. Due to security protocols, the file content is hidden.")
96
+ except Exception as e:
97
+ st.error(f"An error occurred while reading docx: {e}")
98
+ text = None
99
+ else:
100
+ st.warning("Unsupported file type.")
101
+ text = None
102
+
103
+ st.divider()
104
+
105
+ # --- Results Button and Processing Logic ---
106
+ if st.button("Results"):
107
+ if not comet_initialized:
108
+ st.warning("Comet ML not initialized. Check environment variables if you wish to log data.")
109
+
110
+ if st.session_state['file_upload_attempts'] >= max_attempts:
111
+ st.error(f"You have requested results {max_attempts} times. You have reached your daily request limit.")
112
+ st.stop()
113
+
114
+ if text is None:
115
+ st.warning("Please upload a supported file (.pdf or .docx) before requesting results.")
116
+ st.stop()
117
+
118
+ st.session_state['file_upload_attempts'] += 1
119
+
120
+ with st.spinner("Analyzing text...", show_time=True):
121
+ # Load model (cached)
122
+ model = load_ner_model()
123
+ labels = ["work", "booking number", "personally identifiable information", "driver licence", "person", "book", "full address", "company", "actor", "character", "email", "passport number", "Social Security Number", "phone number"]
124
+ text_entities = model.predict_entities(text, labels)
125
+
126
+
127
+
128
+
129
+ df = pd.DataFrame(text_entities)
130
+
131
+ # Clean and filter DataFrame
132
+ pattern = r'[^\w\s]'
133
+ df['word'] = df['word'].replace(pattern, '', regex=True)
134
+ df = df.replace('', 'Unknown').dropna()
135
+
136
+ if df.empty:
137
+ st.warning("No entities were extracted from the uploaded text.")
138
+ st.stop()
139
+
140
+ if comet_initialized:
141
+ experiment = Experiment(
142
+ api_key=COMET_API_KEY,
143
+ workspace=COMET_WORKSPACE,
144
+ project_name=COMET_PROJECT_NAME,
145
+ )
146
+ experiment.log_parameter("input_text_length", len(text))
147
+ experiment.log_table("predicted_entities", df)
148
+
149
+ # --- Display Results ---
150
+ properties = {"border": "2px solid gray", "color": "blue", "font-size": "16px"}
151
+ df_styled = df.style.set_properties(**properties)
152
+ st.dataframe(df_styled, use_container_width=True)
153
+
154
+ with st.expander("See Glossary of tags"):
155
+ st.write('''
156
+ '**word**': ['entity extracted from your text data']
157
+
158
+ '**score**': ['accuracy score; how accurately a tag has been assigned to a given entity']
159
+
160
+ '**entity_group**': ['label (tag) assigned to a given extracted entity']
161
+
162
+ '**start**': ['index of the start of the corresponding entity']
163
+
164
+ '**end**': ['index of the end of the corresponding entity']
165
+
166
+ '**B**'- (Beginning): Indicates the beginning of a given entity.
167
+
168
+ '**I**'- (Inside): Indicates a word that is inside a given entity but not the first one.
169
+
170
+ '**E**'- (End): Indicates the end of a given entity.
171
+
172
+ '**S**'- (Single): Indicates that a given entity is a single entity. It's both the beginning and the end.
173
+
174
+ '**O**' (Outside): Indicates that a word is outside of any named entity.
175
+
176
+ ''')
177
+
178
+ # --- Visualizations ---
179
+ st.subheader("Tree map", divider="rainbow")
180
+ fig_treemap = px.treemap(df, path=[px.Constant("all"), 'word', 'entity_group'],
181
+ values='score', color='entity_group')
182
+ fig_treemap.update_layout(margin=dict(t=50, l=25, r=25, b=25))
183
+ st.plotly_chart(fig_treemap)
184
+ if comet_initialized:
185
+ experiment.log_figure(figure=fig_treemap, figure_name="entity_treemap")
186
+
187
+ value_counts1 = df['entity_group'].value_counts()
188
+ final_df_counts = value_counts1.reset_index().rename(columns={"index": "entity_group"})
189
+
190
+ col1, col2 = st.columns(2)
191
+ with col1:
192
+ st.subheader("Pie Chart", divider="rainbow")
193
+ fig_pie = px.pie(final_df_counts, values='count', names='entity_group', hover_data=['count'], labels={'count': 'count'}, title='Percentage of predicted labels')
194
+ fig_pie.update_traces(textposition='inside', textinfo='percent+label')
195
+ st.plotly_chart(fig_pie)
196
+ if comet_initialized:
197
+ experiment.log_figure(figure=fig_pie, figure_name="label_pie_chart")
198
+
199
+ with col2:
200
+ st.subheader("Bar Chart", divider="rainbow")
201
+ fig_bar = px.bar(final_df_counts, x="count", y="entity_group", color="entity_group", text_auto=True, title='Occurrences of predicted labels')
202
+ st.plotly_chart(fig_bar)
203
+ if comet_initialized:
204
+ experiment.log_figure(figure=fig_bar, figure_name="label_bar_chart")
205
+
206
+ # --- Downloadable Content ---
207
+ dfa = pd.DataFrame(
208
+ data={
209
+ 'word': ['entity extracted from your text data'],
210
+ 'score': ['accuracy score; how accurately a tag has been assigned to a given entity'],
211
+ 'entity_group': ['label (tag) assigned to a given extracted entity'],
212
+ 'start': ['index of the start of the corresponding entity'],
213
+ 'end': ['index of the end of the corresponding entity'],
214
+ 'B(Beginning)': ['Indicates the beginning of a given entity.'],
215
+ 'I(Inside)': ['Indicates a word that is inside a given entity but not the first one.'],
216
+ 'E(End)': ['Indicates the end of a given entity.'],
217
+ 'S(Single)': ['Indicates that a given entity is a single entity.'],
218
+ '0(Outside)': ['Indicates that a word is outside of any named entity.']
219
+
220
+
221
+
222
+
223
+ })
224
+
225
+ buf = io.BytesIO()
226
+ with zipfile.ZipFile(buf, "w") as myzip:
227
+ myzip.writestr("Summary of the results.csv", df.to_csv(index=False))
228
+ myzip.writestr("Glossary of tags.csv", dfa.to_csv(index=False))
229
+
230
+ with stylable_container(
231
+ key="download_button",
232
+ css_styles="""button { background-color: yellow; border: 1px solid black; padding: 5px; color: black; }""",
233
+ ):
234
+ st.download_button(
235
+ label="Download zip file",
236
+ data=buf.getvalue(),
237
+ file_name="nlpblogs_ner_results.zip",
238
+ mime="application/zip",
239
+ )
240
+ if comet_initialized:
241
+ experiment.log_asset(buf.getvalue(), file_name="downloadable_results.zip")
242
+
243
+ st.divider()
244
+ if comet_initialized:
245
+ experiment.end()
246
+
247
+ st.write(f"Number of times you requested results: **{st.session_state['file_upload_attempts']}/{max_attempts}**")