geethareddy commited on
Commit
6e16961
·
verified ·
1 Parent(s): b1cfb42

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +206 -304
app.py CHANGED
@@ -1,331 +1,233 @@
1
- from simple_salesforce import Salesforce
2
- import logging
3
- from flask import Flask, render_template_string, request, jsonify, redirect, url_for
4
- import speech_recognition as sr
5
- from tempfile import NamedTemporaryFile
6
  import os
7
- import ffmpeg
8
- from werkzeug.exceptions import BadRequest
9
  from gtts import gTTS
 
 
 
10
  import time
 
 
 
11
 
12
  app = Flask(__name__)
13
- logging.basicConfig(level=logging.DEBUG)
14
 
15
- # Salesforce connection setup with error handling
16
- def connect_to_salesforce():
17
- try:
18
- sf = Salesforce(
19
- username='diggavalli98@gmail.com',
20
- password='Sati@1020',
21
- security_token='sSSjyhInIsUohKpG8sHzty2q',
22
- consumer_key='3MVG9WVXk15qiz1JbtW1tT9a7Wnkos2RuGamw6p1lC5uPescT5NB2nPygpo6rQ87K1T.zBEn.wR.A6JdgHnIU',
23
- consumer_secret='A75C6B7801D5D20BED0E46631CF58C4F7FF28E4DAF442FE667553D29C35C0451'
24
- )
25
- logging.info("Successfully connected to Salesforce")
26
- return sf
27
- except Exception as e:
28
- logging.error(f"Error connecting to Salesforce: {str(e)}")
29
- return None
30
 
31
- sf = connect_to_salesforce()
 
 
32
 
33
- # Global variables
34
- cart = [] # To store items, quantities, and prices
35
- MENU = {}
 
 
 
 
 
36
 
37
- current_category = None
38
- current_item = None
39
- awaiting_quantity = False
 
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
- # Fetching menu items dynamically from Salesforce
42
- def get_menu_items():
43
- menu_items = {}
44
  try:
45
- query = "SELECT Name, Category__c, Price__c, Ingredients__c FROM Menu_Item__c"
46
- results = sf.query_all(query)
47
-
48
- # Organizing the menu items by category
49
- for record in results['records']:
50
- category = record['Category__c']
51
- item_name = record['Name']
52
- price = record['Price__c']
53
-
54
- if category not in menu_items:
55
- menu_items[category] = {}
56
- menu_items[category][item_name] = price
57
-
58
- return menu_items
59
  except Exception as e:
60
- logging.error(f"Error fetching menu items from Salesforce: {str(e)}")
61
- return {}
62
-
63
- MENU = get_menu_items()
64
-
65
- # HTML Template for Frontend (for register, login, and menu pages)
66
- html_code = """
67
- <!DOCTYPE html>
68
- <html lang="en">
69
- <head>
70
- <meta charset="UTF-8">
71
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
72
- <title>Register / Login</title>
73
- <style>
74
- body {
75
- font-family: Arial, sans-serif;
76
- text-align: center;
77
- background-color: #f4f4f9;
78
- }
79
- h1 {
80
- color: #333;
81
- }
82
- .status, .response {
83
- margin-top: 20px;
84
- }
85
- </style>
86
- </head>
87
- <body>
88
- <h1>Welcome to Biryani Hub</h1>
89
- <div id="welcomePage">
90
- <button onclick="startRegistration()">New Customer</button>
91
- <button onclick="startLogin()">Existing Customer</button>
92
- </div>
93
-
94
- <div id="registrationPage" style="display: none;">
95
- <h2>Register</h2>
96
- <form action="/register" method="POST">
97
- <label for="name">Name</label><br>
98
- <input type="text" id="name" name="name" required><br>
99
- <label for="email">Email</label><br>
100
- <input type="email" id="email" name="email" required><br>
101
- <label for="phone">Phone</label><br>
102
- <input type="text" id="phone" name="phone" required><br>
103
- <button type="submit">Register</button>
104
- </form>
105
- </div>
106
-
107
- <div id="loginPage" style="display: none;">
108
- <h2>Login</h2>
109
- <form action="/login" method="POST">
110
- <label for="email">Email</label><br>
111
- <input type="email" id="email" name="email" required><br>
112
- <label for="phone">Phone</label><br>
113
- <input type="text" id="phone" name="phone" required><br>
114
- <button type="submit">Login</button>
115
- </form>
116
- </div>
117
-
118
- <div id="menuPage" style="display: none;">
119
- <h1>AI Dining Assistant</h1>
120
- <div class="status" id="status">Starting interaction...</div>
121
- <div class="response" id="response" style="display: none;">Response will appear here...</div>
122
- <button onclick="showMenu()">Show Menu</button>
123
- </div>
124
-
125
- <script>
126
- function startRegistration() {
127
- document.getElementById('welcomePage').style.display = 'none';
128
- document.getElementById('registrationPage').style.display = 'block';
129
- }
130
-
131
- function startLogin() {
132
- document.getElementById('welcomePage').style.display = 'none';
133
- document.getElementById('loginPage').style.display = 'block';
134
- }
135
-
136
- function showMenu() {
137
- document.getElementById('menuPage').style.display = 'block';
138
- const categories = {{ MENU | tojson }};
139
- let menuText = "We have the following categories: ";
140
- for (let category in categories) {
141
- menuText += category + " ";
142
- }
143
- document.getElementById('status').innerText = menuText;
144
- }
145
- </script>
146
- </body>
147
- </html>
148
- """
149
 
 
150
  @app.route("/")
151
  def index():
152
- return render_template_string(html_code)
153
-
154
- # Register Page
155
- @app.route("/register", methods=["GET", "POST"])
156
- def register():
157
- if request.method == "POST":
158
- name = request.form.get('name')
159
- email = request.form.get('email')
160
- phone = request.form.get('phone')
161
- if not name or not email or not phone:
162
- logging.error("Missing required fields: Name, Email, Phone.")
163
- return jsonify({"error": "All fields are required."}), 400
164
-
165
- if not sf:
166
- logging.error("Salesforce connection failed.")
167
- return jsonify({"error": "Salesforce connection failed."}), 500
168
-
169
- # Create a record in Salesforce
170
- try:
171
- customer_login = sf.Customer_Login__c.create({
172
- 'Name': name,
173
- 'Email__c': email,
174
- 'Phone_Number__c': phone
175
- })
176
- logging.info(f"Customer registered successfully with ID: {customer_login['id']}")
177
- return redirect(url_for('menu')) # Redirect to menu after successful registration
178
- except Exception as e:
179
- logging.error(f"Error during registration: {str(e)}")
180
- return jsonify({'error': f'Failed to create record in Salesforce: {str(e)}'}), 500
181
-
182
- return render_template_string(html_code)
183
-
184
- # Login Page
185
- @app.route("/login", methods=["GET", "POST"])
186
  def login():
187
- if request.method == "POST":
188
- email = request.form.get('email')
189
- phone = request.form.get('phone')
190
-
191
- if not email or not phone:
192
- logging.error("Missing required fields: Email, Phone.")
193
- return jsonify({"error": "Email and phone are required."}), 400
194
-
195
- if not sf:
196
- logging.error("Salesforce connection failed.")
197
- return jsonify({"error": "Salesforce connection failed."}), 500
198
-
199
- # Check if the customer exists in Salesforce
200
- try:
201
- customer_query = sf.query(f"SELECT Id, Name FROM Customer_Login__c WHERE Email__c = '{email}' AND Phone_Number__c = '{phone}'")
202
- if customer_query['totalSize'] == 0:
203
- logging.error("Customer not found.")
204
- return jsonify({"error": "Customer not found."}), 404
205
- logging.info("Customer logged in successfully")
206
- return redirect(url_for('menu')) # Redirect to menu after successful login
207
- except Exception as e:
208
- logging.error(f"Error during login: {str(e)}")
209
- return jsonify({'error': f'Failed to query Salesforce: {str(e)}'}), 500
210
-
211
- return render_template_string(html_code)
212
-
213
- # Menu Page
214
- @app.route("/menu")
215
- def menu():
216
- return render_template_string(html_code)
217
-
218
- @app.route("/process-audio", methods=["POST"])
219
- def process_audio():
220
- global current_category, current_item, awaiting_quantity
221
  try:
222
- audio_file = request.files.get("audio")
223
- if not audio_file:
224
- raise BadRequest("No audio file provided.")
 
225
 
226
- temp_file = NamedTemporaryFile(delete=False, suffix=".webm")
227
- audio_file.save(temp_file.name)
 
 
 
 
228
 
229
- if os.path.getsize(temp_file.name) == 0:
230
- raise BadRequest("Uploaded audio file is empty.")
231
 
232
- converted_file = NamedTemporaryFile(delete=False, suffix=".wav")
233
- ffmpeg.input(temp_file.name).output(
234
- converted_file.name, acodec="pcm_s16le", ac=1, ar="16000"
235
- ).run(overwrite_output=True)
 
 
 
 
 
 
 
 
 
 
236
 
237
- recognizer = sr.Recognizer()
238
- with sr.AudioFile(converted_file.name) as source:
239
- audio_data = recognizer.record(source)
240
- try:
241
- command = recognizer.recognize_google(audio_data)
242
- logging.info(f"Recognized command: {command}")
243
- response = process_command(command)
244
- except sr.UnknownValueError:
245
- response = "Sorry, I couldn't understand your command. Could you please repeat?"
246
- except sr.RequestError as e:
247
- response = f"Error with the speech recognition service: {e}"
248
-
249
- return jsonify({"response": response})
250
-
251
- except BadRequest as br:
252
- return jsonify({"response": f"Bad Request: {str(br)}"}), 400
253
  except Exception as e:
254
- logging.error(f"Error processing audio: {str(e)}")
255
- return jsonify({"response": f"An error occurred: {str(e)}"}), 500
256
- finally:
257
- os.unlink(temp_file.name)
258
- os.unlink(converted_file.name)
259
-
260
- def process_command(command):
261
- global cart, MENU, current_category, current_item, awaiting_quantity
262
- command = command.lower()
263
-
264
- # Handle quantity input
265
- if awaiting_quantity:
266
- quantity = extract_quantity(command)
267
- if quantity:
268
- cart.append((current_item, MENU[current_category][current_item], quantity))
269
- awaiting_quantity = False
270
- item = current_item
271
- current_item = None
272
- total = sum(i[1] * i[2] for i in cart)
273
- cart_summary = ", ".join([f"{i[0]} x{i[2]} (₹{i[1] * i[2]})" for i in cart])
274
- return f"Added {quantity} x {item} to your cart. Your current cart: {cart_summary}. Total: ₹{total}. Would you like to add more items?"
275
- else:
276
- return "Sorry, I couldn't understand the quantity. Please provide a valid quantity."
277
-
278
- # Handle category selection
279
- for category, items in MENU.items():
280
- if category.lower() in command:
281
- current_category = category
282
- item_list = ", ".join([f"{item} (₹{price})" for item, price in items.items()])
283
- return f"{category} menu: {item_list}. What would you like to order?"
284
-
285
- # Handle item selection with dynamic matching
286
- if current_category:
287
- for item in MENU[current_category].keys():
288
- if item.lower().startswith(command) or command in item.lower():
289
- current_item = item
290
- awaiting_quantity = True
291
- return f"How many quantities of {current_item} would you like?"
292
-
293
- # Handle item removal
294
- if "remove" in command:
295
- for item in cart:
296
- if item[0].lower() in command:
297
- cart.remove(item)
298
- total = sum(i[1] * i[2] for i in cart)
299
- cart_summary = ", ".join([f"{i[0]} x{i[2]} (₹{i[1] * i[2]})" for i in cart])
300
- return f"Removed {item[0]} from your cart. Updated cart: {cart_summary}. Total: ₹{total}."
301
- return "The item you are trying to remove is not in your cart."
302
-
303
- # Handle final order
304
- if "final order" in command or "submit" in command or "Proceed" in command or "place the order" in command:
305
- if cart:
306
- order_details = ", ".join([f"{item[0]} x{item[2]} (₹{item[1] * item[2]})" for item in cart])
307
- total = sum(item[1] * item[2] for item in cart)
308
- cart.clear()
309
- return f"Your final order is: {order_details}. Total price: ₹{total}. Your order will arrive soon, Thank you for visiting Biryani Hub!"
310
- else:
311
- return "Your cart is empty. Please add items before placing the final order."
312
-
313
- # Handle cart details
314
- if "cart details" in command:
315
- if cart:
316
- cart_summary = "\n".join([f"{i[0]} x{i[2]} (₹{i[1] * i[2]})" for i in cart])
317
- total = sum(i[1] * i[2] for i in cart)
318
- return f"Your cart contains:\n{cart_summary}\nTotal: ₹{total}."
319
  else:
320
- return "Your cart is empty."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
 
322
- # Handle menu request
323
- if "menu" in command or "yes" in command or "yeah" in command:
324
- categories = ", ".join(MENU.keys())
325
- return f"We have the following categories: {categories}. Please select a category to proceed."
326
 
327
- # Default response
328
- return "Sorry, I didn't understand that. Please try again."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
329
 
 
330
  if __name__ == "__main__":
331
- app.run(host="0.0.0.0", port=7860)
 
1
+ import torch
2
+ from flask import Flask, render_template, request, jsonify, redirect
3
+ import json
 
 
4
  import os
5
+ from transformers import pipeline
 
6
  from gtts import gTTS
7
+ from pydub import AudioSegment
8
+ from pydub.silence import detect_nonsilent
9
+ from transformers import AutoConfig # Import AutoConfig for the config object
10
  import time
11
+ from waitress import serve
12
+ from simple_salesforce import Salesforce
13
+ import requests # Import requests for exception handling
14
 
15
  app = Flask(__name__)
 
16
 
17
+ # Use whisper-small for faster processing and better speed
18
+ device = "cuda" if torch.cuda.is_available() else "cpu"
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
+ # Create config object to set timeout and other parameters
21
+ config = AutoConfig.from_pretrained("openai/whisper-small")
22
+ config.update({"timeout": 60}) # Set timeout to 60 seconds
23
 
24
+ # Salesforce connection details
25
+ try:
26
+ print("Attempting to connect to Salesforce...")
27
+ sf = Salesforce(username='diggavalli98@gmail.com', password='Sati@1020', security_token='sSSjyhInIsUohKpG8sHzty2q')
28
+ print("Connected to Salesforce successfully!")
29
+ print("User Info:", sf.UserInfo) # Log the user info to verify the connection
30
+ except Exception as e:
31
+ print(f"Failed to connect to Salesforce: {str(e)}")
32
 
33
+ # Functions for Salesforce operations
34
+ def create_salesforce_record(sf, name, email, phone_number):
35
+ try:
36
+ customer_login = sf.Customer_Login__c.create({
37
+ 'Name': name,
38
+ 'Email__c': email,
39
+ 'Phone_Number__c': phone_number
40
+ })
41
+ return customer_login
42
+ except Exception as e:
43
+ raise Exception(f"Failed to create record: {str(e)}")
44
+
45
+ def get_menu_items(sf):
46
+ query = "SELECT Name, Price__c, Ingredients__c, Category__c FROM Menu_Item__c"
47
+ result = sf.query(query)
48
+ return result['records']
49
 
50
+ # Voice-related functions
51
+ def generate_audio_prompt(text, filename):
 
52
  try:
53
+ tts = gTTS(text)
54
+ tts.save(os.path.join("static", filename))
55
+ except gtts.tts.gTTSError as e:
56
+ print(f"Error: {e}")
57
+ print("Retrying after 5 seconds...")
58
+ time.sleep(5) # Wait for 5 seconds before retrying
59
+ generate_audio_prompt(text, filename)
60
+
61
+ # Utility functions
62
+ def convert_to_wav(input_path, output_path):
63
+ try:
64
+ audio = AudioSegment.from_file(input_path)
65
+ audio = audio.set_frame_rate(16000).set_channels(1) # Convert to 16kHz, mono
66
+ audio.export(output_path, format="wav")
67
  except Exception as e:
68
+ print(f"Error: {str(e)}")
69
+ raise Exception(f"Audio conversion failed: {str(e)}")
70
+
71
+ def is_silent_audio(audio_path):
72
+ audio = AudioSegment.from_wav(audio_path)
73
+ nonsilent_parts = detect_nonsilent(audio, min_silence_len=500, silence_thresh=audio.dBFS-16) # Reduced silence duration
74
+ print(f"Detected nonsilent parts: {nonsilent_parts}")
75
+ return len(nonsilent_parts) == 0 # If no speech detected
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
 
77
+ # Routes and Views
78
  @app.route("/")
79
  def index():
80
+ return render_template("index.html")
81
+
82
+ @app.route("/dashboard", methods=["GET"])
83
+ def dashboard():
84
+ return render_template("dashboard.html") # Render the dashboard template
85
+
86
+ @app.route('/login', methods=['POST'])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  def login():
88
+ # Get data from voice bot (name, email, phone number)
89
+ data = request.json # Assuming voice bot sends JSON data
90
+ name = data.get('name')
91
+ email = data.get('email')
92
+ phone_number = data.get('phone_number')
93
+
94
+ if not name or not email or not phone_number:
95
+ return jsonify({'error': 'Missing required fields'}), 400
96
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  try:
98
+ customer_login = create_salesforce_record(sf, name, email, phone_number)
99
+ return redirect("/menu") # Redirect to the menu page after successful login
100
+ except Exception as e:
101
+ return jsonify({'error': f'Failed to create record in Salesforce: {str(e)}'}), 500
102
 
103
+ @app.route("/submit", methods=["POST"])
104
+ def submit():
105
+ data = request.json
106
+ name = data.get('name')
107
+ email = data.get('email')
108
+ phone = data.get('phone')
109
 
110
+ if not name or not email or not phone:
111
+ return jsonify({'error': 'Missing data'}), 400
112
 
113
+ try:
114
+ # Create Salesforce record
115
+ customer_login = sf.Customer_Login__c.create({
116
+ 'Name': name,
117
+ 'Email__c': email,
118
+ 'Phone_Number__c': phone
119
+ })
120
+
121
+ if customer_login.get('id'):
122
+ print(f"Success: Customer record created with ID: {customer_login['id']}")
123
+ return jsonify({'success': True})
124
+ else:
125
+ print("Failed: No ID returned after creating the record.")
126
+ return jsonify({'error': 'Failed to create record'}), 500
127
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  except Exception as e:
129
+ print(f"Error: {str(e)}") # Print error message if an exception occurs
130
+ return jsonify({'error': str(e)}), 500
131
+
132
+
133
+ @app.route("/menu", methods=["GET"])
134
+ def menu_page():
135
+ menu_items = get_menu_items(sf) # Fetch menu items from Salesforce
136
+ menu_data = [{"name": item['Name'], "price": item['Price__c'], "ingredients": item['Ingredients__c'], "category": item['Category__c']} for item in menu_items]
137
+ return render_template("menu_page.html", menu_items=menu_data)
138
+
139
+ # Route for handling order
140
+ @app.route("/order", methods=["POST"])
141
+ def place_order():
142
+ item_name = request.json.get('item_name')
143
+ quantity = request.json.get('quantity')
144
+ order_data = {"Item__c": item_name, "Quantity__c": quantity}
145
+ sf.Order__c.create(order_data)
146
+ return jsonify({"success": True, "message": f"Order for {item_name} placed successfully."})
147
+
148
+ # Route to handle the cart
149
+ @app.route("/cart", methods=["GET"])
150
+ def cart():
151
+ cart_items = [] # Placeholder for cart items
152
+ return render_template("cart_page.html", cart_items=cart_items)
153
+
154
+ # Route for the order summary page
155
+ @app.route("/order-summary", methods=["GET"])
156
+ def order_summary():
157
+ order_details = [] # Placeholder for order details
158
+ return render_template("order_summary.html", order_details=order_details)
159
+
160
+ @app.route("/transcribe", methods=["POST"])
161
+ def transcribe():
162
+ if "audio" not in request.files:
163
+ print("No audio file provided")
164
+ return jsonify({"error": "No audio file provided"}), 400
165
+
166
+ audio_file = request.files["audio"]
167
+ input_audio_path = os.path.join("static", "temp_input.wav")
168
+ output_audio_path = os.path.join("static", "temp.wav")
169
+ audio_file.save(input_audio_path)
170
+
171
+ try:
172
+ # Convert to WAV
173
+ convert_to_wav(input_audio_path, output_audio_path)
174
+
175
+ # Check for silence
176
+ if is_silent_audio(output_audio_path):
177
+ return jsonify({"error": "No speech detected. Please try again."}), 400
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  else:
179
+ print("Audio contains speech, proceeding with transcription.")
180
+
181
+ # Use Whisper ASR model for transcription
182
+ result = None
183
+ retry_attempts = 3
184
+ for attempt in range(retry_attempts):
185
+ try:
186
+ result = pipeline("automatic-speech-recognition", model="openai/whisper-small", device=0 if torch.cuda.is_available() else -1, config=config)
187
+ print(f"Transcribed text: {result['text']}")
188
+ break
189
+ except requests.exceptions.ReadTimeout:
190
+ print(f"Timeout occurred, retrying attempt {attempt + 1}/{retry_attempts}...")
191
+ time.sleep(5)
192
+
193
+ if result is None:
194
+ return jsonify({"error": "Unable to transcribe audio after retries."}), 500
195
 
196
+ transcribed_text = result["text"].strip().capitalize()
197
+ print(f"Transcribed text: {transcribed_text}")
 
 
198
 
199
+ # Extract name, email, and phone number from the transcribed text
200
+ parts = transcribed_text.split()
201
+ name = parts[0] if len(parts) > 0 else "Unknown Name"
202
+ email = parts[1] if '@' in parts[1] else "unknown@domain.com"
203
+ phone_number = parts[2] if len(parts) > 2 else "0000000000"
204
+ print(f"Parsed data - Name: {name}, Email: {email}, Phone Number: {phone_number}")
205
+
206
+ # Confirm details before submission
207
+ confirmation = f"Is this correct? Name: {name}, Email: {email}, Phone: {phone_number}"
208
+ generate_audio_prompt(confirmation, "confirmation.mp3")
209
+
210
+ # Simulate confirmation via user action
211
+ user_confirms = True # Assuming the user confirms, you can replace this with actual user input logic
212
+
213
+ if user_confirms:
214
+ # Create record in Salesforce
215
+ salesforce_response = create_salesforce_record(name, email, phone_number)
216
+
217
+ # Log the Salesforce response
218
+ print(f"Salesforce record creation response: {salesforce_response}")
219
+
220
+ # Check if the response contains an error
221
+ if "error" in salesforce_response:
222
+ print(f"Error creating record in Salesforce: {salesforce_response['error']}")
223
+ return jsonify(salesforce_response), 500
224
+
225
+ return jsonify({"text": transcribed_text, "salesforce_record": salesforce_response})
226
+
227
+ except Exception as e:
228
+ print(f"Error in transcribing or processing: {str(e)}")
229
+ return jsonify({"error": f"Speech recognition error: {str(e)}"}), 500
230
 
231
+ # Start Production Server
232
  if __name__ == "__main__":
233
+ serve(app, host="0.0.0.0", port=7860)