File size: 9,563 Bytes
e040241
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import streamlit as st
import pandas as pd
from transformers import pipeline
import torch  # For GPU checks
import numpy as np
from groq import Groq
import os
import time

# Set page config
st.set_page_config(
    page_title="Restaurant Review Analyzer 🍽️",
    page_icon="πŸ”",
    layout="wide"
)

# Custom CSS
st.markdown("""
    <style>
    .main {
        padding: 2rem;
    }
    .stProgress > div > div > div {
        background-color: #1f77b4;
    }
    .metric-card {
        background-color: #f8f9fa;
        padding: 1rem;
        border-radius: 0.5rem;
        box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    }
    </style>
""", unsafe_allow_html=True)


def setup_classifier():
    """Initialize the zero-shot classification pipeline with GPU support if available"""
    with st.spinner('Loading classification model... βš™οΈ'):
        device = 0 if torch.cuda.is_available() else -1  # Use GPU if available
        return pipeline(
            "zero-shot-classification",
            model="joeddav/xlm-roberta-large-xnli",
            device=device
        )


def create_aspect_labels():
    """Create labels for all aspects with positive/negative sentiment"""
    aspects = [
        "food quality",
        "service",
        "ambiance",
        "price",
        "cleanliness",
        "portion size",
        "wait time",
        "menu variety"
    ]

    sentiment_labels = []
    for aspect in aspects:
        sentiment_labels.extend([
            f"positive {aspect}",
            f"negative {aspect}"
        ])

    return aspects, sentiment_labels


def classify_review(classifier, review, sentiment_labels):
    """Classify a single review across all aspects and sentiments"""
    if pd.isna(review) or not isinstance(review, str):
        return {label: 0 for label in sentiment_labels}

    try:
        result = classifier(
            review,
            sentiment_labels,
            multi_label=True
        )
        return dict(zip(result['labels'], result['scores']))
    except Exception as e:
        st.error(f"Error processing review: {e}")
        return {label: 0 for label in sentiment_labels}

def format_summary_for_llm(summary_df):
    """Format the classification summary into a clear text prompt"""
    summary_text = "Restaurant Reviews Analysis Summary:\n\n"

    sentiment_analysis = {}
    for aspect in summary_df.index:
        pos = summary_df.loc[aspect, 'positive_mentions']
        neg = summary_df.loc[aspect, 'negative_mentions']
        total = pos + neg
        if total > 0:
            pos_percent = (pos / total) * 100
            neg_percent = (neg / total) * 100
            difference = pos_percent - neg_percent
            sentiment_analysis[aspect] = {
                'difference': difference,
                'positive_percent': pos_percent,
                'negative_percent': neg_percent,
                'total_mentions': total
            }

    for aspect, metrics in sentiment_analysis.items():
        summary_text += f"{aspect}:\n"
        summary_text += f"- Total Mentions: {metrics['total_mentions']}\n"
        summary_text += f"- Positive Mentions: {metrics['positive_percent']:.1f}%\n"
        summary_text += f"- Negative Mentions: {metrics['negative_percent']:.1f}%\n"
        summary_text += f"- Sentiment Difference: {metrics['difference']:.1f}%\n"
        summary_text += "\n"

    return summary_text

def generate_insights(groq_client, summary_text):
    """Generate insights using Groq API"""
    prompt = f"""You are an expert restaurant consultant analyzing customer feedback data.
    Based on the following customer review analysis summary, provide actionable insights
    and recommendations for the restaurant owner. When analyzing the data:

    - If an aspect has a positive difference of 0.5% or more, consider it a strength
    - If an aspect has a negative difference of 0.5% or more, consider it an area for improvement
    - For differences smaller than 0.5%, consider the aspect neutral or mixed
    - Pay special attention to aspects with high total mentions as they represent stronger customer sentiment

    Analysis Data:
    {summary_text}

    Please provide:
    1. Key Strengths: What's working well (aspects with >0.5% positive difference)
    2. Areas for Improvement: What needs attention (aspects with >0.5% negative difference)
    3. Mixed Reception Areas: Aspects with minimal difference (<0.5%) between positive and negative
    4. Actionable Recommendations: Specific steps based on the analysis
    5. Priority Actions: What should be addressed first, considering both sentiment differences and total mention count

    Format your response in clear sections with bullet points where appropriate.
    Add relevant emojis to make the response more engaging.
    """

    try:
        with st.spinner('Generating insights... πŸ€”'):
            chat_completion = groq_client.chat.completions.create(
                messages=[{"role": "user", "content": prompt}],
                model="mixtral-8x7b-32768",
                temperature=0.7,
                max_tokens=1500,
            )
            return chat_completion.choices[0].message.content
    except Exception as e:
        st.error(f"Error generating insights: {str(e)}")
        return None

def main():
    # Header
    st.title("🍽️ Restaurant Review Analyzer")
    st.markdown("### Transform your customer feedback into actionable insights! πŸ“Š")

    # Sidebar
    st.sidebar.header("πŸ“ Configuration")

    # File upload
    uploaded_file = st.sidebar.file_uploader("Upload your CSV file", type=['csv'])

    if uploaded_file is not None:
        # Read CSV
        try:
            df = pd.read_csv(uploaded_file)
            df.columns = df.columns.str.strip()

            # Validate 'Review' column
            if 'Review' not in df.columns:
                st.error("❌ 'Review' column not found in the CSV file!")
                return

            # Show sample of uploaded data
            st.subheader("πŸ“‹ Sample Reviews")
            st.dataframe(df[['Review']].head(5), use_container_width=True)

            # Process reviews
            if st.button("πŸš€ Analyze Reviews"):
                # Initialize classifier
                classifier = setup_classifier()
                aspects, sentiment_labels = create_aspect_labels()

                # Process reviews with progress bar
                results = []
                progress_bar = st.progress(0)
                status_text = st.empty()

                for idx, review in enumerate(df['Review'].head(30)):
                    status_text.text(f"Processing review {idx + 1}/30...")
                    scores = classify_review(classifier, review, sentiment_labels)
                    results.append(scores)
                    progress_bar.progress((idx + 1) / 30)

                results_df = pd.DataFrame(results)

                # Analyze results
                summary = pd.DataFrame()
                for aspect in aspects:
                    pos_col = f"positive {aspect}"
                    neg_col = f"negative {aspect}"

                    summary.loc[aspect, 'positive_mentions'] = (results_df[pos_col] > 0.5).sum()
                    summary.loc[aspect, 'negative_mentions'] = (results_df[neg_col] > 0.5).sum()
                    summary.loc[aspect, 'avg_positive_score'] = results_df[pos_col].mean()
                    summary.loc[aspect, 'avg_negative_score'] = results_df[neg_col].mean()

                # Display summary in columns
                st.subheader("πŸ“Š Analysis Summary")
                col1, col2 = st.columns(2)

                with col1:
                    st.markdown("#### πŸ“ˆ Positive Mentions")
                    for aspect in aspects:
                        st.metric(
                            label=aspect.title(),
                            value=f"{summary.loc[aspect, 'positive_mentions']} reviews",
                            delta=f"{summary.loc[aspect, 'avg_positive_score']:.2%} avg. confidence"
                        )

                with col2:
                    st.markdown("#### πŸ“‰ Negative Mentions")
                    for aspect in aspects:
                        st.metric(
                            label=aspect.title(),
                            value=f"{summary.loc[aspect, 'negative_mentions']} reviews",
                            delta=f"{summary.loc[aspect, 'avg_negative_score']:.2%} avg. confidence",
                            delta_color="inverse"
                        )

                # Generate insights
                groq_client = Groq(api_key="groq_api_key")

                summary_text = format_summary_for_llm(summary)
                insights = generate_insights(groq_client, summary_text)

                if insights:
                    st.subheader("πŸ’‘ Key Insights and Recommendations")
                    st.markdown(insights)

        except Exception as e:
            st.error(f"Error processing file: {str(e)}")

    else:
        # Show welcome message and instructions
        st.markdown("""
        ### πŸ‘‹ Welcome to the Restaurant Review Analyzer!

        To get started:
        1. πŸ“ Upload your CSV file containing customer reviews
        2. πŸ” Make sure your file has a 'Review' column
        3. πŸš€ Click 'Analyze Reviews' to process the data
        4. πŸ“Š Get detailed insights and recommendations

        The analyzer will process the reviews to provide quick insights!
        """)


if __name__ == "__main__":
    main()