darthPanda/SentimentAnalysisTool
0
1import streamlit as st2import os.path3import pathlib4 5import pandas as pd6import numpy as np7import PyPDF28from PyPDF2 import PdfReader9from os import walk10import nltk11import glob12 13import plotly.express as px14from wordcloud import WordCloud15import plotly.io as pio16from plotly.subplots import make_subplots17import plotly.graph_objs as go18import pandas as pd19import plotly.offline as pyo20 21import io22from io import StringIO23 24#@st.cache_resource()25@st.cache()26def get_nl():27 return nltk.download('punkt')28get_nl()29 30from nltk.tokenize import sent_tokenize31from transformers import AutoTokenizer, AutoModelForSequenceClassification32from transformers import pipeline33 34# if os.path.exists("report.html"):35# os.remove("report.html")36 37 38#@st.cache_resource()39@st.cache(allow_output_mutation=True)40def get_sentiment_model():41 tokenizer = AutoTokenizer.from_pretrained("ProsusAI/finbert")42 model = AutoModelForSequenceClassification.from_pretrained("ProsusAI/finbert")43 return tokenizer,model44 45tokenizer_sentiment,model_sentiment = get_sentiment_model()46 47@st.cache(allow_output_mutation=True)48def get_emotion_model():49 tokenizer = AutoTokenizer.from_pretrained("j-hartmann/emotion-english-distilroberta-base")50 model = AutoModelForSequenceClassification.from_pretrained("j-hartmann/emotion-english-distilroberta-base")51 return tokenizer,model52 53tokenizer_emotion,model_emotion = get_emotion_model()54 55@st.cache(allow_output_mutation=True)56def get_intent_model():57 classifier = pipeline("zero-shot-classification", model='cross-encoder/nli-deberta-v3-small')58 return classifier59 60intent_classifier = get_intent_model()61 62def extract_text_from_pdf(path):63 text=''64 reader = PdfReader(path)65 number_of_pages = len(reader.pages)66 print(number_of_pages)67 for i in range(number_of_pages):68 page=reader.pages[i]69 text = text + page.extract_text()70 return text71 72# Create a button to download the HTML file73def download_html():74 with st.spinner('Downloading HTML file...'):75 # Get the HTML content76 with open('report.html', "r") as f:77 html = f.read()78 f.close()79 # Set the file name and content type80 file_name = "report.html"81 mime_type = "text/html"82 # Use st.download_button() to create a download button83 print('download button')84 st.download_button(label="Download Report", data=html, file_name=file_name, mime=mime_type)85 st.stop()86 87if 'filename_key' not in st.session_state:88 st.session_state.filename_key = ''89 90st.write("""91# Dcoument Analysis Tool92""")93#uploaded_file = st.file_uploader("Choose a PDF file")94#uploaded_file = st.file_uploader("Choose a PDF file", accept_multiple_files=False, type=['pdf'])95uploaded_file = st.file_uploader("Choose a PDF file", accept_multiple_files=True, type=['pdf'])96#if uploaded_file is not None:97if len(uploaded_file)==0:98 #print('none')99 st.session_state.filename_key = ''100elif len(uploaded_file)>0:101 import time102 # Wait for 5 seconds103 time.sleep(5)104 105 pdf_reader = PyPDF2.PdfReader(uploaded_file[0])106 num_pages = len(pdf_reader.pages)107 file_name = uploaded_file[0].name108 109 # st.write(st.session_state.filename_key)110 # print(file_name)111 # st.write("Filename:", file_name)112 if num_pages > 20:113 st.error("Pages in PDF file should be less than 20.")114 # Check that only one file was uploaded115 #elif isinstance(uploaded_file, list):116 elif len(uploaded_file) > 1:117 st.error("Please upload only one PDF file at a time.")118 elif st.session_state.filename_key == file_name:119 st.write("Report downloaded successfully")120 else:121 #uploaded_file = uploaded_file[0]122 # Check that the file is a PDF123 if uploaded_file[0].type != 'application/pdf':124 st.error("Please upload a PDF file.")125 else:126 127 ############################ 1. Extract text from PDF ############################128 text=''129 # return text from pdf130 pdf_reader = PyPDF2.PdfReader(uploaded_file[0])131 # Get the number of pages in the PDF file132 num_pages = len(pdf_reader.pages)133 # Display the number of pages in the PDF file134 st.write(f"Number of pages in PDF file: {num_pages}")135 for i in range(num_pages):136 page=pdf_reader.pages[i]137 text = text + page.extract_text()138 139 140 141 ############################ 2. Running models ############################142 text = text.replace("\n", " " )143 text = text.replace("$", "dollar " )144 sentences = sent_tokenize(text)145 title = sentences[0]146 long_sentence=[]147 small_sentence=[]148 useful_sentence=[]149 for i in sentences:150 if len(i) > 510:151 long_sentence.append(i)152 elif len(i) < 50:153 small_sentence.append(i)154 else:155 useful_sentence.append(i)156 157 useful_sentence_len = len(useful_sentence)158 del sentences159 160 ############################ 2.1 Sentiment Modeling ############################161 placeholder1 = st.empty()162 placeholder1.text('Performing Sentiment Analysis...')163 164 #with st.empty():165 my_bar = st.progress(0)166 tokenizer = tokenizer_sentiment167 model = model_sentiment168 pipe = pipeline(model="ProsusAI/finbert") 169 classifier = pipeline(model="ProsusAI/finbert") 170 #output = classifier(useful_sentence)171 output=[]172 i=0173 for temp in useful_sentence:174 output.extend(classifier(temp))175 i=i+1176 my_bar.progress(int((i/useful_sentence_len)*100))177 178 my_bar.empty()179 df = pd.DataFrame.from_dict(output)180 df['Sentence']= pd.Series(useful_sentence)181 182 ############################ 2.2 Emotion Modeling ############################183 #placeholder2 = st.empty()184 placeholder1.text('Performing Emotion Analysis...')185 186# with st.empty():187 my_bar = st.progress(0)188 tokenizer = tokenizer_emotion189 model = model_emotion190 classifier = pipeline("text-classification", model="j-hartmann/emotion-english-distilroberta-base", top_k=1)191 output_emotion = []192 i=0193 for temp in useful_sentence:194 output_emotion.extend(classifier(temp)[0])195 i=i+1196 my_bar.progress(int((i/useful_sentence_len)*100))197 198 my_bar.empty()199 placeholder1.text('Emotion Analysis Completed')200 201 ############################ 2.3 Intent Modeling ############################202 placeholder1.text('Performing Intent Analysis...')203 204 my_bar = st.progress(0)205 candidate_labels = ['complaint', 'suggestion', 'query']206 classifier = intent_classifier207 # temp_intent = classifier(useful_sentence, candidate_labels)208 # output_intent=[]209 # for temp in temp_intent:210 # output_intent.append({'label' : temp['labels'][0], 'score' : temp['scores'][0]})211 output_intent=[]212 i=0213 for temp1 in useful_sentence:214 temp = classifier(temp1, candidate_labels)215 output_intent.append({'label' : temp['labels'][0], 'score' : temp['scores'][0]})216 i=i+1217 my_bar.progress(int((i/useful_sentence_len)*100))218 df_intent = pd.DataFrame.from_dict(output_intent)219 df_intent['Sentence']= pd.Series(useful_sentence)220 221 my_bar.empty()222 placeholder1.text('Processing Completed')223 224 225 226 ############################ 3. Processing ############################227 228 ############################ 3.1. Sentiment Analysis ############################229 # labels = ['neutral', 'positive', 'negative']230 # values = df.label.value_counts().to_list()231 232 labels = ['neutral', 'positive', 'negative']233 values = [df[df['label']=='neutral'].shape[0], df[df['label']=='positive'].shape[0], df[df['label']=='negative'].shape[0]]234 235 236 # removing words237 words_to_remove = ["s", "quarter", "thank", "million", "Thank", "quetion", 'wa', 'rate', 'firt',238 "customer", "business", "last year", "year", 'lat', 'well', 'jut', 'thi', 'cutomer',239 "will", "think", "higher", "question", "going"]240 for word in words_to_remove:241 text = text.replace(word, "")242 wordcloud = WordCloud(background_color='white', width=800, height=400).generate(text)243 image = wordcloud.to_image()244 245 pos_df = df[df['label']=='positive']246 pos_df = pos_df[['score', 'Sentence']]247 pos_df = pos_df.sort_values('score', ascending=False)248 pos_df_mean = pos_df.score.mean()249 pos_df['score'] = pos_df['score'].round(4)250 pos_df.rename(columns = {'Sentence':'Positive Sentences'}, inplace = True)251 num_of_pos_sentences = pos_df.shape[0]252 if num_of_pos_sentences == 0:253 pos_df.loc[0] = [0.0, '-------No positive sentences found in report-------'] 254 255 neg_df = df[df['label']=='negative']256 neg_df = neg_df[['score', 'Sentence']]257 neg_df = neg_df.sort_values('score', ascending=False)258 neg_df_mean = neg_df.score.mean()259 neg_df['score'] = neg_df['score'].round(4)260 neg_df.rename(columns = {'Sentence':'Negative Sentences'}, inplace = True)261 num_of_neg_sentences = neg_df.shape[0]262 if num_of_neg_sentences == 0:263 neg_df.loc[0] = [0.0, '-------No negative sentences found in report-------']264 265 neu_df = df[df['label']=='neutral']266 neu_df = neu_df[['score', 'Sentence']]267 neu_df = neu_df.sort_values('score', ascending=False)268 #neu_df_mean = neu_df.score.mean()269 neu_df['score'] = neu_df['score'].round(4)270 neu_df.rename(columns = {'Sentence':'Neutral Sentences'}, inplace = True)271 num_of_neu_sentences = neu_df.shape[0]272 if num_of_neu_sentences == 0:273 neu_df.loc[0] = [0.0, '-------No neutral sentences found in report-------']274 275 # df_temp = neg_df276 # df_temp = df_temp['score'] * -1277 # df_temp = pd.concat([df_temp, pos_df])278 df_temp = neg_df279 df_temp['score'] = df_temp['score'] * -1280 df_temp_list = df_temp['score'].to_list() + pos_df['score'].to_list()281 282 mean = sum(df_temp_list) / len(df_temp_list)283 284 ############################ 3.2. Emotion Analysis ############################285 286 df_emotion = pd.DataFrame.from_dict(output_emotion)287 df_emotion['Sentence']= pd.Series(useful_sentence)288 289 df_joy = df_emotion[df_emotion['label']=='joy']290 df_joy = df_joy[['score', 'Sentence']]291 df_joy = df_joy.sort_values('score', ascending=False)292 df_joy['score'] = df_joy['score'].round(4)293 df_joy.rename(columns = {'Sentence':'Joy Sentences'}, inplace = True)294 num_of_joy_sentences = df_joy.shape[0]295 if num_of_joy_sentences == 0:296 df_joy.loc[0] = [0.0, '-------No joy sentences found in report-------']297 298 df_sadness = df_emotion[df_emotion['label']=='sadness']299 df_sadness = df_sadness[['score', 'Sentence']]300 df_sadness = df_sadness.sort_values('score', ascending=False)301 df_sadness['score'] = df_sadness['score'].round(4)302 df_sadness.rename(columns = {'Sentence':'Sad Sentences'}, inplace = True)303 num_of_sad_sentences = df_sadness.shape[0]304 if num_of_sad_sentences == 0:305 df_sadness.loc[0] = [0.0, '-------No sad sentences found in report-------']306 307 df_anger = df_emotion[df_emotion['label']=='anger']308 df_anger = df_anger[['score', 'Sentence']]309 df_anger = df_anger.sort_values('score', ascending=False)310 df_anger['score'] = df_anger['score'].round(4)311 df_anger.rename(columns = {'Sentence':'Angry Sentences'}, inplace = True)312 num_of_anger_sentences = df_anger.shape[0]313 if num_of_anger_sentences == 0:314 df_anger.loc[0] = [0.0, '-------No angry sentences found in report-------']315 316 df_surprise = df_emotion[df_emotion['label']=='surprise']317 df_surprise = df_surprise[['score', 'Sentence']]318 df_surprise = df_surprise.sort_values('score', ascending=False)319 df_surprise['score'] = df_surprise['score'].round(4)320 df_surprise.rename(columns = {'Sentence':'Surprised Sentences'}, inplace = True)321 num_of_surprise_sentences = df_surprise.shape[0]322 if num_of_surprise_sentences == 0:323 df_surprise.loc[0] = [0.0, '-------No surprised sentences found in report-------']324 325 # df_temp_emotion = df_sadness326 # df_temp_emotion = pd.concat([df_sadness, df_anger])327 # df_temp_emotion = df_temp_emotion['score'] * -1328 # df_temp_emotion = pd.concat([df_temp_emotion, df_joy])329 330 df_temp_emotion = df_sadness331 df_temp_emotion['score'] = df_temp_emotion['score'] * -1332 df_temp_emotion_list = df_temp_emotion['score'].to_list() + df_joy['score'].to_list()333 emotion_mean = sum(df_temp_emotion_list) / len(df_temp_emotion_list)334 335 # df_temp = neg_df336 # df_temp['score'] = df_temp['score'] * -1337 # df_temp_list = df_temp['score'].to_list() + pos_df['score'].to_list()338 339 # mean = sum(df_temp_list) / len(df_temp_list)340 341 342 ############################ 3.3. Intent Analysis ############################343 df_query = df_intent[df_intent['label']=='query']344 df_query = df_query[['score', 'Sentence']]345 df_query = df_query.sort_values('score', ascending=False)346 df_query['score'] = df_query['score'].round(4)347 df_query.rename(columns = {'Sentence':'Queries'}, inplace = True)348 df_query = df_query[df_query['score']>0.5]349 num_of_queries = df_query.shape[0]350 if num_of_queries == 0:351 df_query.loc[0] = [0.0, '-------No queries found in report-------']352 353 df_complaint = df_intent[df_intent['label']=='complaint']354 df_complaint = df_complaint[['score', 'Sentence']]355 df_complaint = df_complaint.sort_values('score', ascending=False)356 df_complaint['score'] = df_complaint['score'].round(4)357 df_complaint.rename(columns = {'Sentence':'Complaints'}, inplace = True)358 df_complaint = df_complaint[df_complaint['score']>0.5]359 num_of_complaints = df_complaint.shape[0]360 if num_of_complaints == 0:361 df_complaint.loc[0] = [0.0, '-------No complaints found in report-------']362 363 df_suggestion = df_intent[df_intent['label']=='suggestion']364 df_suggestion = df_suggestion[['score', 'Sentence']]365 df_suggestion = df_suggestion.sort_values('score', ascending=False)366 df_suggestion['score'] = df_suggestion['score'].round(4)367 df_suggestion.rename(columns = {'Sentence':'Suggestions'}, inplace = True)368 df_suggestion = df_suggestion[df_suggestion['score']>0.5]369 num_of_suggestions = df_suggestion.shape[0]370 if num_of_suggestions == 0:371 df_suggestion.loc[0] = [0.0, '-------No suggestions found in report-------']372 373 total_num_of_intent = num_of_queries + num_of_complaints + num_of_suggestions374 375 376 377 ############################ 4. Plotting ############################378 379 fig = make_subplots(380 rows=62, cols=6,381 specs=[ [None, None, None, None, None, None],382 [None, None, None, None, None, None],383 [None, None, None, None, None, None],384 [None, None, {"type": "indicator", "rowspan": 3, "colspan": 2}, None, None, None],385 [None, None, None, None, None, None],386 [{"type": "pie", "rowspan": 6, "colspan": 2}, None, {"type": "indicator", "rowspan": 6, "colspan": 2}, None, {"type": "indicator", "rowspan": 6, "colspan": 2}, None],387 [None, None, None, None, None, None],388 [None, None, None, None, None, None],389 [None, None, None, None, None, None],390 [None, None, None, None, None, None],391 [None, None, None, None, None, None],392 [None, None, None, None, None, None],393 [{"type": "image", "rowspan": 5, "colspan": 3}, None, None, {"type": "table", "rowspan": 5, "colspan": 3}, None, None],394 [None, None, None, None, None, None],395 [None, None, None, None, None, None],396 [None, None, None, None, None, None],397 [None, None, None, None, None, None],398 [{"type": "table", "rowspan": 5, "colspan": 3}, None, None, {"type": "table", "rowspan": 5, "colspan": 3}, None, None],399 [None, None, None, None, None, None],400 [None, None, None, None, None, None],401 [None, None, None, None, None, None],402 [None, None, None, None, None, None],403 [None, None, None, None, None, None],404 [None, None, None, None, None, None],405 [None, None, None, None, None, None],406 [None, None, {"type": "indicator", "rowspan": 3, "colspan": 2}, None, None, None],407 [None, None, None, None, None, None],408 [None, None, None, None, None, None],409 [{"type": "bar", "rowspan": 6, "colspan": 6}, None, None, None, None, None],410 [None, None, None, None, None, None],411 [None, None, None, None, None, None],412 [None, None, None, None, None, None],413 [None, None, None, None, None, None],414 [None, None, None, None, None, None],415 [None, None, None, None, None, None],416 [{"type": "table", "rowspan": 2, "colspan": 3}, None, None, {"type": "table", "rowspan": 2, "colspan": 3}, None, None],417 [None, None, None, None, None, None],418 [None, None, None, None, None, None],419 [{"type": "table", "rowspan": 2, "colspan": 3}, None, None, {"type": "table", "rowspan": 2, "colspan": 3}, None, None],420 [None, None, None, None, None, None],421 [None, None, None, None, None, None],422 [None, None, None, None, None, None],423 [None, None, None, None, None, None],424 [None, None, {"type": "indicator", "rowspan": 3, "colspan": 2}, None, None, None],425 [None, None, None, None, None, None],426 [None, None, None, None, None, None],427 [None, {"type": "indicator", "rowspan": 2, "colspan": 5}, None, None, None, None],#first bullet428 [None, None, None, None, None, None],429 [None, None, None, None, None, None],430 [None, {"type": "indicator", "rowspan": 2, "colspan": 5}, None, None, None, None], #2nd bullet431 [None, None, None, None, None, None],432 [None, None, None, None, None, None],433 [None, {"type": "indicator", "rowspan": 2, "colspan": 5}, None, None, None, None],434 [None, None, None, None, None, None],435 [None, None, None, None, None, None],436 [{"type": "table", "rowspan": 4, "colspan": 2}, None, {"type": "table", "rowspan": 4, "colspan": 2}, None, {"type": "table", "rowspan": 4, "colspan": 2}, None],437 [None, None, None, None, None, None],438 [None, None, None, None, None, None],439 [None, None, None, None, None, None],440 [None, None, None, None, None, None],441 [None, None, None, None, None, None],442 [None, None, None, None, None, None],443 ],444 )445 446 ############################ 4.1. Sentiment Analysis ############################447 448 fig.add_trace(go.Indicator(449 mode = "number",450 value = int(mean*100),451 number = {"suffix": "%"},452 title = {"text": "<span style='font-size:1.5em'>Sentiment Analysis</span><br><span style='font-size:0.8em;color:gray'>Positivity Score</span>"}453 ), row=4, col=3)454 455 colors = px.colors.diverging.Portland#RdBu456 fig.add_trace(go.Pie(labels=labels, values=values, hole = 0.5,457 title = 'Count by label', 458 marker=dict(colors=colors,459 line=dict(width=2, color='white'))),460 row=6, col=1)461 462 463 fig.add_trace(go.Indicator(464 mode = "number",465 value = len(df.label.values.tolist()),466 title = {"text": "Count of Sentence"}), row=6, col=3)467 #fig.update_traces(title_text="Sentiment Analysis", selector=dict(type='indicator'), row=6, col=3)468 469 fig.add_trace(go.Indicator(470 mode = "gauge+number",471 value = mean,472 domain = {'x': [0, 1], 'y': [0, 1]},473 title = {'text': "Average of Score", 'font': {'size': 16}},474 gauge = {475 'axis': {'range': [-1, 1], 'tickwidth': 1, 'tickcolor': "darkblue"}, 476 'bar': {'color': "darkblue"},477 'steps': [478 {'range': [-0.29, 0.29], 'color': 'white'},479 {'range': [0.3, 1], 'color': 'green'},480 {'range': [-1, -0.3], 'color': 'red'}481 ],482 'threshold': {483 'line': {'color': "black", 'width': 4},484 'thickness': 0.75,485 'value': abs((pos_df_mean - neg_df_mean))486 }487 }488 ), row=6, col=5)489 490 if mean < -0.29:491 fig.update_traces(title_text="Cummulative Sentiment Negative", selector=dict(type='indicator'), row=6, col=5)492 elif mean < 0.29:493 fig.update_traces(title_text="Cummulative Sentiment Neutral", selector=dict(type='indicator'), row=6, col=5)494 else:495 fig.update_traces(title_text="Cummulative Sentiment Positive", selector=dict(type='indicator'), row=6, col=5)496 497 fig.add_trace(go.Image(z=image), row=13, col=1)498 fig.update_xaxes(visible=False, row=13, col=1)499 fig.update_yaxes(visible=False, row=13, col=1)500 501 table_trace1 = go.Table(502 header=dict(values=list(pos_df.columns), fill_color='lightgray', align='left'),503 cells=dict(values=[pos_df[name] for name in pos_df.columns], fill_color='white', align='left'),504 columnwidth=[1, 4]505 )506 fig.add_trace(table_trace1, row=13, col=4)507 508 table_trace2 = go.Table(509 header=dict(values=list(neg_df.columns), fill_color='lightgray', align='left'),510 cells=dict(values=[neg_df[name] for name in neg_df.columns], fill_color='white', align='left'),511 columnwidth=[1, 4]512 )513 fig.add_trace(table_trace2, row=18, col=4)514 515 table_trace2 = go.Table(516 header=dict(values=list(neu_df.columns), fill_color='lightgray', align='left'),517 cells=dict(values=[neu_df[name] for name in neu_df.columns], fill_color='white', align='left'),518 columnwidth=[1, 4]519 )520 fig.add_trace(table_trace2, row=18, col=1)521 522 523 524 ########################### 4.2. Emotion Analysis ###########################525 526 fig.add_trace(go.Indicator(527 mode = "number",528 value = int(emotion_mean*100),529 number = {"suffix": "%"},530 title = {"text": "<span style='font-size:1.5em'>Emotion Analysis</span><br><span style='font-size:0.8em;color:gray'>Happiness Score</span>"}531 ), row=26, col=3)532 533 # Add bar chart534 colors_emotions = ['#174ecf', '#cfc517', '#940625', '#17cfcb']535 emotion_bar_xlabels = ['Joy', 'Sadness', 'Anger', 'Surprise']536 emotion_bar_ylabels = [num_of_joy_sentences, 537 num_of_sad_sentences,538 num_of_anger_sentences,539 num_of_surprise_sentences]540 #annotations = [dict(x=x, y=y, text='๐', showarrow=False) for x, y in zip(emotion_bar_xlabels, emotion_bar_ylabels)]541 annotations = ['๐', '๐', '๐ก', '๐ฏ']542 fig.add_trace(543 go.Bar(x=emotion_bar_xlabels, y= emotion_bar_ylabels, 544 showlegend=True,545 marker_color=colors_emotions,546 text=annotations,547 textfont=dict(size=40)),548 row=29, col=1)549 fig.update_xaxes(title_text='Emotions', title_font=dict(size=16), row=29, col=1)550 fig.update_yaxes(title_text='Number of sentences', title_font=dict(size=16), row=29, col=1)551 552 # df_anger.loc[0] = [0.0, 'None']553 # df_anger554 ################## happiness table555 table_trace2 = go.Table(556 header=dict(values=list(df_joy.columns), fill_color='lightgray', align='left'),557 cells=dict(values=[df_joy[name] for name in df_joy.columns], fill_color='white', align='left'),558 columnwidth=[1, 4]559 )560 fig.add_trace(table_trace2, row=36, col=1)561 562 ################## sadness table563 table_trace2 = go.Table(564 header=dict(values=list(df_sadness.columns), fill_color='lightgray', align='left'),565 cells=dict(values=[df_sadness[name] for name in df_sadness.columns], fill_color='white', align='left'),566 columnwidth=[1, 4]567 )568 fig.add_trace(table_trace2, row=36, col=4)569 570 ################## surprise table571 table_trace2 = go.Table(572 header=dict(values=list(df_surprise.columns), fill_color='lightgray', align='left'),573 cells=dict(values=[df_surprise[name] for name in df_surprise.columns], fill_color='white', align='left'),574 columnwidth=[1, 4]575 )576 fig.add_trace(table_trace2, row=39, col=1)577 578 ################## anger table579 table_trace2 = go.Table(580 header=dict(values=list(df_anger.columns), fill_color='lightgray', align='left'),581 cells=dict(values=[df_anger[name] for name in df_anger.columns], fill_color='white', align='left'),582 columnwidth=[1, 4]583 )584 fig.add_trace(table_trace2, row=39, col=4)585 586 587 588 ########################### 4.3. Intent Analysis ###########################589 590 fig.add_trace(go.Indicator(591 mode = "number",592 value = round(num_of_suggestions/max(num_of_complaints,0), 2), 593 number = {"suffix": ""},594 title = {"text": "<span style='font-size:1.5em'>Intent Analysis</span><br><span style='font-size:0.8em;color:gray'>Suggestion/Complaint Ratio</span>"}595 ), row=44, col=3)596 597 fig.add_trace(go.Indicator(598 mode = "number+gauge",599 gauge = {'shape': "bullet", 'axis': {'range': [None, total_num_of_intent]}, 'bar': {'color': "blue"}},600 #delta = {'reference': 300},601 value = num_of_queries,602 #domain = {'x': [0.5, 1], 'y': [0.3, 0.9]},603 title = {'text': "Queries"}), row=47, col=2)604 605 fig.add_trace(go.Indicator(606 mode = "number+gauge",607 gauge = {'shape': "bullet", 'axis': {'range': [None, total_num_of_intent]},},608 #delta = {'reference': 300},609 value = num_of_suggestions,610 #domain = {'x': [0.5, 1], 'y': [0.3, 0.9]},611 title = {'text': "Suggestions"}), row=50, col=2)612 613 fig.add_trace(go.Indicator(614 mode = "number+gauge",615 gauge = {'shape': "bullet", 'axis': {'range': [None, total_num_of_intent]}, 'bar': {'color': "red"}},616 #delta = {'reference': 300},617 value = num_of_complaints,618 #domain = {'x': [0.5, 1], 'y': [0.3, 0.9]},619 title = {'text': "Complaints"}), row=53, col=2)620 621 ############ query table622 table_trace2 = go.Table(623 header=dict(values=list(df_query.columns), fill_color='lightgray', align='left'),624 cells=dict(values=[df_query[name] for name in df_query.columns], fill_color='white', align='left'),625 columnwidth=[1, 4]626 )627 fig.add_trace(table_trace2, row=56, col=1)628 629 ############ complaints table630 table_trace2 = go.Table(631 header=dict(values=list(df_complaint.columns), fill_color='lightgray', align='left'),632 cells=dict(values=[df_complaint[name] for name in df_complaint.columns], fill_color='white', align='left'),633 columnwidth=[1, 4]634 )635 fig.add_trace(table_trace2, row=56, col=3)636 637 ############ suggestions table638 table_trace2 = go.Table(639 header=dict(values=list(df_suggestion.columns), fill_color='lightgray', align='left'),640 cells=dict(values=[df_suggestion[name] for name in df_suggestion.columns], fill_color='white', align='left'),641 columnwidth=[1, 4]642 )643 fig.add_trace(table_trace2, row=56, col=5)644 645 import textwrap646 if len(title) > 120:647 title = title[:120] + '...'648 wrapped_title = "\n".join(textwrap.wrap(title, width=50))649 650 # Add HTML tags to force line breaks in the title text651 wrapped_title = "<br>".join(wrapped_title.split("\n"))652 653 fig.update_layout(height=4000, showlegend=False, title={'text': f"<b>{wrapped_title} - Text Analysis Report</b>", 'x': 0.5, 'xanchor': 'center','font': {'size': 32}})654 655 656 #pyo.plot(fig, filename='report.html')657 658 ############################## 5. Download Report ##############################659 660 buffer = io.StringIO()661 fig.write_html(buffer, include_plotlyjs='cdn')662 html_bytes = buffer.getvalue().encode()663 664 st.download_button(665 label='Download Report',666 data=html_bytes,667 file_name='report.html',668 mime='text/html'669 )670 671 st.session_state.filename_key = file_name