CoolFace
Apppublic

darthPanda/SentimentAnalysisTool

sourceHugging Faceopenrailupdated 4y agoView on Hugging Face
0likes
app.py671 linesDownload Raw Back to root
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