Zasha1 commited on
Commit
5689c2b
·
verified ·
1 Parent(s): 00882a0

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +295 -0
app.py ADDED
@@ -0,0 +1,295 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import speech_recognition as sr
2
+ from sentiment_analysis import analyze_sentiment
3
+ from product_recommender import ProductRecommender
4
+ from objection_handler import ObjectionHandler
5
+ from google_sheets import fetch_call_data, store_data_in_sheet
6
+ from sentence_transformers import SentenceTransformer
7
+ from env_setup import config
8
+ import re
9
+ import uuid
10
+ from google.oauth2 import service_account
11
+ from googleapiclient.discovery import build
12
+ import pandas as pd
13
+ import plotly.express as px
14
+ import plotly.graph_objs as go
15
+ import streamlit as st
16
+
17
+ # Initialize components
18
+ product_recommender = ProductRecommender(r"C:\Users\shaik\Downloads\Sales Calls Transcriptions - Sheet2.csv")
19
+ objection_handler = ObjectionHandler(r"C:\Users\shaik\Downloads\Sales Calls Transcriptions - Sheet3.csv")
20
+ model = SentenceTransformer('all-MiniLM-L6-v2')
21
+
22
+ def generate_comprehensive_summary(chunks):
23
+ """
24
+ Generate a comprehensive summary from conversation chunks
25
+ """
26
+ # Extract full text from chunks
27
+ full_text = " ".join([chunk[0] for chunk in chunks])
28
+
29
+ # Perform basic analysis
30
+ total_chunks = len(chunks)
31
+ sentiments = [chunk[1] for chunk in chunks]
32
+
33
+ # Determine overall conversation context
34
+ context_keywords = {
35
+ 'product_inquiry': ['dress', 'product', 'price', 'stock'],
36
+ 'pricing': ['cost', 'price', 'budget'],
37
+ 'negotiation': ['installment', 'payment', 'manage']
38
+ }
39
+
40
+ # Detect conversation themes
41
+ themes = []
42
+ for keyword_type, keywords in context_keywords.items():
43
+ if any(keyword.lower() in full_text.lower() for keyword in keywords):
44
+ themes.append(keyword_type)
45
+
46
+ # Basic sentiment analysis
47
+ positive_count = sentiments.count('POSITIVE')
48
+ negative_count = sentiments.count('NEGATIVE')
49
+ neutral_count = sentiments.count('NEUTRAL')
50
+
51
+ # Key interaction highlights
52
+ key_interactions = []
53
+ for chunk in chunks:
54
+ if any(keyword.lower() in chunk[0].lower() for keyword in ['price', 'dress', 'stock', 'installment']):
55
+ key_interactions.append(chunk[0])
56
+
57
+ # Construct summary
58
+ summary = f"Conversation Summary:\n"
59
+
60
+ # Context and themes
61
+ if 'product_inquiry' in themes:
62
+ summary += "• Customer initiated a product inquiry about items.\n"
63
+
64
+ if 'pricing' in themes:
65
+ summary += "• Price and budget considerations were discussed.\n"
66
+
67
+ if 'negotiation' in themes:
68
+ summary += "• Customer and seller explored flexible payment options.\n"
69
+
70
+ # Sentiment insights
71
+ summary += f"\nConversation Sentiment:\n"
72
+ summary += f"• Positive Interactions: {positive_count}\n"
73
+ summary += f"• Negative Interactions: {negative_count}\n"
74
+ summary += f"• Neutral Interactions: {neutral_count}\n"
75
+
76
+ # Key highlights
77
+ summary += "\nKey Conversation Points:\n"
78
+ for interaction in key_interactions[:3]: # Limit to top 3 key points
79
+ summary += f"• {interaction}\n"
80
+
81
+ # Conversation outcome
82
+ if positive_count > negative_count:
83
+ summary += "\nOutcome: Constructive and potentially successful interaction."
84
+ elif negative_count > positive_count:
85
+ summary += "\nOutcome: Interaction may require further follow-up."
86
+ else:
87
+ summary += "\nOutcome: Neutral interaction with potential for future engagement."
88
+
89
+ return summary
90
+
91
+ def is_valid_input(text):
92
+ text = text.strip().lower()
93
+ if len(text) < 3 or re.match(r'^[a-zA-Z\s]*$', text) is None:
94
+ return False
95
+ return True
96
+
97
+ def is_relevant_sentiment(sentiment_score):
98
+ return sentiment_score > 0.4
99
+
100
+ def calculate_overall_sentiment(sentiment_scores):
101
+ if sentiment_scores:
102
+ average_sentiment = sum(sentiment_scores) / len(sentiment_scores)
103
+ overall_sentiment = (
104
+ "POSITIVE" if average_sentiment > 0 else
105
+ "NEGATIVE" if average_sentiment < 0 else
106
+ "NEUTRAL"
107
+ )
108
+ else:
109
+ overall_sentiment = "NEUTRAL"
110
+ return overall_sentiment
111
+
112
+ def real_time_analysis():
113
+ recognizer = sr.Recognizer()
114
+ mic = sr.Microphone()
115
+
116
+ st.info("Say 'stop' to end the process.")
117
+
118
+ sentiment_scores = []
119
+ transcribed_chunks = []
120
+ total_text = ""
121
+
122
+ try:
123
+ while True:
124
+ with mic as source:
125
+ st.write("Listening...")
126
+ recognizer.adjust_for_ambient_noise(source)
127
+ audio = recognizer.listen(source)
128
+
129
+ try:
130
+ st.write("Recognizing...")
131
+ text = recognizer.recognize_google(audio)
132
+ st.write(f"*Recognized Text:* {text}")
133
+
134
+ if 'stop' in text.lower():
135
+ st.write("Stopping real-time analysis...")
136
+ break
137
+
138
+ # Append to the total conversation
139
+ total_text += text + " "
140
+ sentiment, score = analyze_sentiment(text)
141
+ sentiment_scores.append(score)
142
+
143
+ # Handle objection
144
+ objection_response = handle_objection(text)
145
+
146
+ # Get product recommendation
147
+ recommendations = []
148
+ if is_valid_input(text) and is_relevant_sentiment(score):
149
+ query_embedding = model.encode([text])
150
+ distances, indices = product_recommender.index.search(query_embedding, 1)
151
+
152
+ if distances[0][0] < 1.5: # Similarity threshold
153
+ recommendations = product_recommender.get_recommendations(text)
154
+
155
+ transcribed_chunks.append((text, sentiment, score))
156
+
157
+ st.write(f"*Sentiment:* {sentiment} (Score: {score})")
158
+ st.write(f"*Objection Response:* {objection_response}")
159
+
160
+ if recommendations:
161
+ st.write("*Product Recommendations:*")
162
+ for rec in recommendations:
163
+ st.write(rec)
164
+
165
+ except sr.UnknownValueError:
166
+ st.error("Speech Recognition could not understand the audio.")
167
+ except sr.RequestError as e:
168
+ st.error(f"Error with the Speech Recognition service: {e}")
169
+ except Exception as e:
170
+ st.error(f"Error during processing: {e}")
171
+
172
+ # After conversation ends, calculate and display overall sentiment and summary
173
+ overall_sentiment = calculate_overall_sentiment(sentiment_scores)
174
+ call_summary = generate_comprehensive_summary(transcribed_chunks)
175
+
176
+ st.subheader("Conversation Summary:")
177
+ st.write(total_text.strip())
178
+ st.subheader("Overall Sentiment:")
179
+ st.write(overall_sentiment)
180
+
181
+ # Store data in Google Sheets
182
+ store_data_in_sheet(
183
+ config["google_sheet_id"],
184
+ transcribed_chunks,
185
+ call_summary,
186
+ overall_sentiment
187
+ )
188
+ st.success("Conversation data stored successfully in Google Sheets!")
189
+
190
+ except Exception as e:
191
+ st.error(f"Error in real-time analysis: {e}")
192
+
193
+ def handle_objection(text):
194
+ query_embedding = model.encode([text])
195
+ distances, indices = objection_handler.index.search(query_embedding, 1)
196
+ if distances[0][0] < 1.5: # Adjust similarity threshold as needed
197
+ responses = objection_handler.handle_objection(text)
198
+ return "\n".join(responses) if responses else "No objection response found."
199
+ return "No objection response found."
200
+
201
+ # (Previous imports remain the same)
202
+
203
+ def run_app():
204
+ st.set_page_config(page_title="Sales Call Assistant", layout="wide")
205
+ st.title("AI Sales Call Assistant")
206
+
207
+ st.sidebar.title("Navigation")
208
+ app_mode = st.sidebar.radio("Choose a mode:", ["Real-Time Call Analysis", "Dashboard"])
209
+
210
+ if app_mode == "Real-Time Call Analysis":
211
+ st.header("Real-Time Sales Call Analysis")
212
+ if st.button("Start Listening"):
213
+ real_time_analysis()
214
+
215
+ elif app_mode == "Dashboard":
216
+ st.header("Call Summaries and Sentiment Analysis")
217
+ try:
218
+ data = fetch_call_data(config["google_sheet_id"])
219
+ if data.empty:
220
+ st.warning("No data available in the Google Sheet.")
221
+ else:
222
+ # Sentiment Visualizations
223
+ sentiment_counts = data['Sentiment'].value_counts()
224
+
225
+ # Pie Chart
226
+ col1, col2 = st.columns(2)
227
+ with col1:
228
+ st.subheader("Sentiment Distribution")
229
+ fig_pie = px.pie(
230
+ values=sentiment_counts.values,
231
+ names=sentiment_counts.index,
232
+ title='Call Sentiment Breakdown',
233
+ color_discrete_map={
234
+ 'POSITIVE': 'green',
235
+ 'NEGATIVE': 'red',
236
+ 'NEUTRAL': 'blue'
237
+ }
238
+ )
239
+ st.plotly_chart(fig_pie)
240
+
241
+ # Bar Chart
242
+ with col2:
243
+ st.subheader("Sentiment Counts")
244
+ fig_bar = px.bar(
245
+ x=sentiment_counts.index,
246
+ y=sentiment_counts.values,
247
+ title='Number of Calls by Sentiment',
248
+ labels={'x': 'Sentiment', 'y': 'Number of Calls'},
249
+ color=sentiment_counts.index,
250
+ color_discrete_map={
251
+ 'POSITIVE': 'green',
252
+ 'NEGATIVE': 'red',
253
+ 'NEUTRAL': 'blue'
254
+ }
255
+ )
256
+ st.plotly_chart(fig_bar)
257
+
258
+ # Existing Call Details Section
259
+ st.subheader("All Calls")
260
+ display_data = data.copy()
261
+ display_data['Summary Preview'] = display_data['Summary'].str[:100] + '...'
262
+ st.dataframe(display_data[['Call ID', 'Chunk', 'Sentiment', 'Summary Preview', 'Overall Sentiment']])
263
+
264
+ # Dropdown to select Call ID
265
+ unique_call_ids = data[data['Call ID'] != '']['Call ID'].unique()
266
+ call_id = st.selectbox("Select a Call ID to view details:", unique_call_ids)
267
+
268
+ # Display selected Call ID details
269
+ call_details = data[data['Call ID'] == call_id]
270
+ if not call_details.empty:
271
+ st.subheader("Detailed Call Information")
272
+ st.write(f"**Call ID:** {call_id}")
273
+ st.write(f"**Overall Sentiment:** {call_details.iloc[0]['Overall Sentiment']}")
274
+
275
+ # Expand summary section
276
+ st.subheader("Full Call Summary")
277
+ st.text_area("Summary:",
278
+ value=call_details.iloc[0]['Summary'],
279
+ height=200,
280
+ disabled=True)
281
+
282
+ # Show all chunks for the selected call
283
+ st.subheader("Conversation Chunks")
284
+ for _, row in call_details.iterrows():
285
+ if pd.notna(row['Chunk']):
286
+ st.write(f"**Chunk:** {row['Chunk']}")
287
+ st.write(f"**Sentiment:** {row['Sentiment']}")
288
+ st.write("---") # Separator between chunks
289
+ else:
290
+ st.error("No details available for the selected Call ID.")
291
+ except Exception as e:
292
+ st.error(f"Error loading dashboard: {e}")
293
+
294
+ if __name__ == "__main__":
295
+ run_app()