akiko19191 commited on
Commit
aecb7e8
·
verified ·
1 Parent(s): dfe4909

Upload folder using huggingface_hub

Browse files
__pycache__/search_engine.cpython-311.pyc CHANGED
Binary files a/__pycache__/search_engine.cpython-311.pyc and b/__pycache__/search_engine.cpython-311.pyc differ
 
app/__pycache__/ai_features.cpython-311.pyc CHANGED
Binary files a/app/__pycache__/ai_features.cpython-311.pyc and b/app/__pycache__/ai_features.cpython-311.pyc differ
 
app/__pycache__/config.cpython-311.pyc CHANGED
Binary files a/app/__pycache__/config.cpython-311.pyc and b/app/__pycache__/config.cpython-311.pyc differ
 
app/__pycache__/xero_client.cpython-311.pyc CHANGED
Binary files a/app/__pycache__/xero_client.cpython-311.pyc and b/app/__pycache__/xero_client.cpython-311.pyc differ
 
app/__pycache__/xero_routes.cpython-311.pyc CHANGED
Binary files a/app/__pycache__/xero_routes.cpython-311.pyc and b/app/__pycache__/xero_routes.cpython-311.pyc differ
 
app/ai_features.py CHANGED
@@ -271,6 +271,18 @@ register_new_customer_function = {
271
  }
272
  }
273
 
 
 
 
 
 
 
 
 
 
 
 
 
274
  get_my_orders_function_website = {
275
  "name": "get_my_orders",
276
  "description": "Retrieves a summary of the user's most recent orders to display in the chat.",
@@ -517,14 +529,15 @@ def whatsapp_reply():
517
  chat_history = history_doc.get('history', []) if history_doc else []
518
 
519
  tools = types.Tool(function_declarations=[
520
- create_direct_order_function, # ADDED
521
  add_items_to_cart_function,
522
  remove_items_from_cart_function,
523
  clear_cart_function,
524
  get_cart_items_function,
525
  place_order_function,
526
  get_my_orders_function,
527
- register_new_customer_function
 
528
  ])
529
 
530
  instruction_prompt = f"""
@@ -542,12 +555,16 @@ def whatsapp_reply():
542
 
543
  3. **Registering New Customers:**
544
  - If the admin asks to register a new customer, use the `register_new_customer` function.
 
 
 
545
 
546
  **Function Guide & Rules:**
547
  - `create_direct_order`: Use for complete, one-shot orders with a delivery date.
548
  - `add_items_to_cart`: To add items for a specific customer when NO delivery date is given.
549
  - `place_order`: To finalize and place a customer's order *from their cart*.
550
  - `register_new_customer`: To sign up a new customer.
 
551
  - **Product List:** {', '.join(product_context_list)}.
552
  - **Company Info:** For general questions: {company_info}.
553
  - **Communication:** Always be professional. Confirm actions clearly. Respond using WhatsApp-compatible markdown (*bold*, _italic_).
@@ -591,6 +608,18 @@ def whatsapp_reply():
591
  details = f"*Details for Order #{order['serial_no']}*\n*Customer:* {order_doc.get('user_email')}\n*Status:* {order_doc.get('status', 'N/A')}\n*Delivery:* {order_doc.get('delivery_date', 'N/A')}\n\n*Items:*\n" + "\n".join(item_lines)
592
  final_response_text = details
593
 
 
 
 
 
 
 
 
 
 
 
 
 
594
  # For all other functions, perform user lookup
595
  else:
596
  user, error_msg = _find_user_by_identifier(args.get("user_identifier"))
@@ -727,20 +756,32 @@ def whatsapp_reply():
727
  current_app.logger.error(f"WhatsApp endpoint error: {e}")
728
  final_response_text = "I'm having a little trouble right now. Please try again in a moment."
729
 
730
- if len(final_response_text) > 1600:
731
- # Find the last space before the 1600 character limit to avoid splitting a word.
732
- split_point = final_response_text[:1600].rfind(' ')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
733
 
734
- # If no space is found (very long word/URL), fall back to a hard split at 1600.
735
- if split_point == -1:
736
- split_point = 1600
737
-
738
- part1 = final_response_text[:split_point]
739
- # Use .strip() to remove any leading space from the second part.
740
- part2 = final_response_text[split_point:].strip()
741
-
742
- twilio_resp.message(part1)
743
- twilio_resp.message("❌TWILLIO ERROR:MESSAGE WAS TOO LONG, SO SOME PARTS OF THE MESSAGE WERE NOT SENT.")
744
  else:
745
  twilio_resp.message(final_response_text)
746
 
 
271
  }
272
  }
273
 
274
+ # +++ START: NEW FUNCTION DEFINITION FOR LISTING ACCOUNTS +++
275
+ list_all_accounts_function = {
276
+ "name": "list_all_accounts",
277
+ "description": "Lists all registered customer accounts with their name, business name, and email. Use this when the admin asks for a list of all users or accounts.",
278
+ "parameters": {
279
+ "type": "object",
280
+ "properties": {}
281
+ }
282
+ }
283
+ # +++ END: NEW FUNCTION DEFINITION FOR LISTING ACCOUNTS +++
284
+
285
+
286
  get_my_orders_function_website = {
287
  "name": "get_my_orders",
288
  "description": "Retrieves a summary of the user's most recent orders to display in the chat.",
 
529
  chat_history = history_doc.get('history', []) if history_doc else []
530
 
531
  tools = types.Tool(function_declarations=[
532
+ create_direct_order_function,
533
  add_items_to_cart_function,
534
  remove_items_from_cart_function,
535
  clear_cart_function,
536
  get_cart_items_function,
537
  place_order_function,
538
  get_my_orders_function,
539
+ register_new_customer_function,
540
+ list_all_accounts_function
541
  ])
542
 
543
  instruction_prompt = f"""
 
555
 
556
  3. **Registering New Customers:**
557
  - If the admin asks to register a new customer, use the `register_new_customer` function.
558
+
559
+ 4. **Listing All Customers:**
560
+ - If the admin asks for a list of all accounts or users, use the `list_all_accounts` function.
561
 
562
  **Function Guide & Rules:**
563
  - `create_direct_order`: Use for complete, one-shot orders with a delivery date.
564
  - `add_items_to_cart`: To add items for a specific customer when NO delivery date is given.
565
  - `place_order`: To finalize and place a customer's order *from their cart*.
566
  - `register_new_customer`: To sign up a new customer.
567
+ - `list_all_accounts`: To get a list of all registered customers.
568
  - **Product List:** {', '.join(product_context_list)}.
569
  - **Company Info:** For general questions: {company_info}.
570
  - **Communication:** Always be professional. Confirm actions clearly. Respond using WhatsApp-compatible markdown (*bold*, _italic_).
 
608
  details = f"*Details for Order #{order['serial_no']}*\n*Customer:* {order_doc.get('user_email')}\n*Status:* {order_doc.get('status', 'N/A')}\n*Delivery:* {order_doc.get('delivery_date', 'N/A')}\n\n*Items:*\n" + "\n".join(item_lines)
609
  final_response_text = details
610
 
611
+ elif function_call.name == 'list_all_accounts':
612
+ all_users = list(mongo.db.users.find({}, {'email': 1, 'contactPerson': 1, 'businessName': 1, '_id': 0}))
613
+ if not all_users:
614
+ final_response_text = "There are no customer accounts registered in the system yet."
615
+ else:
616
+ account_list_text = "*Here is the list of all customer accounts:*\n\n"
617
+ for user in all_users:
618
+ account_list_text += f"- *Name:* {user.get('contactPerson', 'N/A')}\n"
619
+ account_list_text += f" *Business:* {user.get('businessName', 'N/A')}\n"
620
+ account_list_text += f" *Email:* {user.get('email', 'N/A')}\n\n"
621
+ final_response_text = account_list_text.strip()
622
+
623
  # For all other functions, perform user lookup
624
  else:
625
  user, error_msg = _find_user_by_identifier(args.get("user_identifier"))
 
756
  current_app.logger.error(f"WhatsApp endpoint error: {e}")
757
  final_response_text = "I'm having a little trouble right now. Please try again in a moment."
758
 
759
+ # --- 7. Send Response via Twilio ---
760
+ MAX_LENGTH = 1600
761
+ if len(final_response_text) > MAX_LENGTH:
762
+ # Split the message into parts and send them sequentially
763
+ parts = []
764
+ temp_string = final_response_text
765
+ while len(temp_string) > 0:
766
+ if len(temp_string) > MAX_LENGTH:
767
+ # Find the last newline or space to avoid splitting mid-word
768
+ split_point = temp_string[:MAX_LENGTH].rfind('\n')
769
+ if split_point == -1:
770
+ split_point = temp_string[:MAX_LENGTH].rfind('. ')
771
+ if split_point == -1:
772
+ split_point = temp_string[:MAX_LENGTH].rfind(' ')
773
+ # If no natural break point, just split at the max length
774
+ if split_point == -1:
775
+ split_point = MAX_LENGTH
776
+
777
+ parts.append(temp_string[:split_point])
778
+ temp_string = temp_string[split_point:].strip()
779
+ else:
780
+ parts.append(temp_string)
781
+ break
782
 
783
+ for part in parts:
784
+ twilio_resp.message(part)
 
 
 
 
 
 
 
 
785
  else:
786
  twilio_resp.message(final_response_text)
787
 
app/xero_client.py CHANGED
@@ -108,7 +108,16 @@ def initialize_zoho_sdk(grant_token=None, accounts_server_url=None):
108
 
109
  # Store the accounts server URL for refreshing the token later
110
  token_data['accounts_server'] = base_accounts_url
111
-
 
 
 
 
 
 
 
 
 
112
  logger.info(f"Token data received: {token_data}")
113
  token_data['expires_at'] = int(time.time()) + token_data['expires_in']
114
  _save_token(token_data)
@@ -136,11 +145,22 @@ def _get_stored_token():
136
  def _save_token(token_data):
137
  """Saves token data to MongoDB."""
138
  try:
 
 
 
 
 
 
 
 
 
 
139
  mongo.db.zoho_tokens.update_one(
140
  {'_id': 'zoho_oauth_token'},
141
  {'$set': token_data},
142
  upsert=True
143
  )
 
144
  except Exception as e:
145
  logger.error(f"Error saving Zoho token to MongoDB: {e}", exc_info=True)
146
 
@@ -173,7 +193,16 @@ def _refresh_access_token():
173
  # Preserve crucial details not always present in the refresh response
174
  new_token_data['refresh_token'] = new_token_data.get('refresh_token', stored_token['refresh_token'])
175
  new_token_data['accounts_server'] = new_token_data.get('accounts_server', base_accounts_url)
176
- new_token_data['expires_at'] = int(time.time()) + new_token_data['expires_in']
 
 
 
 
 
 
 
 
 
177
 
178
  _save_token(new_token_data)
179
  logger.info("Successfully refreshed and saved new access token.")
@@ -191,6 +220,7 @@ def get_access_token():
191
  return None
192
 
193
  if token.get('expires_at', 0) < time.time() + 60:
 
194
  token = _refresh_access_token()
195
  if not token:
196
  return None
@@ -224,4 +254,4 @@ def zoho_token_required(function):
224
  session['next_url'] = request.url
225
  return redirect(url_for("zoho.login"))
226
  return function(*args, **kwargs)
227
- return decorator
 
108
 
109
  # Store the accounts server URL for refreshing the token later
110
  token_data['accounts_server'] = base_accounts_url
111
+
112
+ # Log and print refresh token if returned
113
+ if 'refresh_token' in token_data:
114
+ logger.info("Received refresh_token from Zoho during token exchange.")
115
+ logger.info(f"refresh_token: {token_data.get('refresh_token')}")
116
+ print(f"--- Received Zoho refresh_token: {token_data.get('refresh_token')} ---")
117
+ else:
118
+ logger.info("No refresh_token present in Zoho response for this exchange.")
119
+ print("--- No refresh_token received from Zoho in this exchange. ---")
120
+
121
  logger.info(f"Token data received: {token_data}")
122
  token_data['expires_at'] = int(time.time()) + token_data['expires_in']
123
  _save_token(token_data)
 
145
  def _save_token(token_data):
146
  """Saves token data to MongoDB."""
147
  try:
148
+ # Preserve an existing refresh_token if the current token_data doesn't contain one.
149
+ existing = mongo.db.zoho_tokens.find_one({'_id': 'zoho_oauth_token'})
150
+ if existing and 'refresh_token' in existing and 'refresh_token' not in token_data:
151
+ # Preserve the previously stored refresh token
152
+ token_data['refresh_token'] = existing['refresh_token']
153
+ logger.info("Preserved existing refresh_token because the new token response did not include one.")
154
+ print("--- Preserved existing Zoho refresh_token (not returned by current response). ---")
155
+
156
+ # Set expires_at if present as int already; otherwise leave as-is
157
+ # Ensure the DB document uses fixed _id
158
  mongo.db.zoho_tokens.update_one(
159
  {'_id': 'zoho_oauth_token'},
160
  {'$set': token_data},
161
  upsert=True
162
  )
163
+ logger.info("Zoho token saved to MongoDB (upsert).")
164
  except Exception as e:
165
  logger.error(f"Error saving Zoho token to MongoDB: {e}", exc_info=True)
166
 
 
193
  # Preserve crucial details not always present in the refresh response
194
  new_token_data['refresh_token'] = new_token_data.get('refresh_token', stored_token['refresh_token'])
195
  new_token_data['accounts_server'] = new_token_data.get('accounts_server', base_accounts_url)
196
+ # Use .get for expires_in to avoid KeyError if absent
197
+ new_token_data['expires_at'] = int(time.time()) + int(new_token_data.get('expires_in', 3600))
198
+
199
+ # Log whether Zoho returned a refresh_token on refresh calls (often not returned).
200
+ if 'refresh_token' in new_token_data and new_token_data.get('refresh_token') != stored_token.get('refresh_token'):
201
+ logger.info("Zoho returned a new refresh_token during refresh.")
202
+ print(f"--- Zoho returned a new refresh_token during refresh: {new_token_data.get('refresh_token')} ---")
203
+ else:
204
+ logger.info("Zoho did not return a new refresh_token during refresh; preserving existing one.")
205
+ print("--- Zoho did not return a new refresh_token during refresh; using stored refresh_token. ---")
206
 
207
  _save_token(new_token_data)
208
  logger.info("Successfully refreshed and saved new access token.")
 
220
  return None
221
 
222
  if token.get('expires_at', 0) < time.time() + 60:
223
+ logger.info("TRYING TO GENERATE A REFRESH TOKEN")
224
  token = _refresh_access_token()
225
  if not token:
226
  return None
 
254
  session['next_url'] = request.url
255
  return redirect(url_for("zoho.login"))
256
  return function(*args, **kwargs)
257
+ return decorator
app/xero_routes.py CHANGED
@@ -61,7 +61,9 @@ def login():
61
  'client_id': config['ZOHO_CLIENT_ID'],
62
  'response_type': 'code',
63
  'access_type': 'offline',
64
- 'redirect_uri': config['ZOHO_REDIRECT_URL']
 
 
65
  }
66
  accounts_url = 'https://accounts.zohocloud.ca/oauth/v2/auth'
67
  auth_url = f"{accounts_url}?{urllib.parse.urlencode(params)}"
@@ -232,4 +234,4 @@ def edit_inventory():
232
  return redirect(url_for("zoho.edit_inventory"))
233
 
234
  products = list(mongo.db.products.find({}, {"_id": 0, "name": 1, "image_url": 1}).sort("name", 1))
235
- return render_template("edit_inventory.html", title="Edit Inventory Image", products=products)
 
61
  'client_id': config['ZOHO_CLIENT_ID'],
62
  'response_type': 'code',
63
  'access_type': 'offline',
64
+ 'redirect_uri': config['ZOHO_REDIRECT_URL'],
65
+ # Force consent so that Zoho returns a refresh token when possible
66
+ 'prompt': 'consent'
67
  }
68
  accounts_url = 'https://accounts.zohocloud.ca/oauth/v2/auth'
69
  auth_url = f"{accounts_url}?{urllib.parse.urlencode(params)}"
 
234
  return redirect(url_for("zoho.edit_inventory"))
235
 
236
  products = list(mongo.db.products.find({}, {"_id": 0, "name": 1, "image_url": 1}).sort("name", 1))
237
+ return render_template("edit_inventory.html", title="Edit Inventory Image", products=products)
flask_session/0e919d76317babaddd6cd255d5683d18 ADDED
Binary file (139 Bytes). View file
 
flask_session/2029240f6d1128be89ddc32729463129 CHANGED
Binary files a/flask_session/2029240f6d1128be89ddc32729463129 and b/flask_session/2029240f6d1128be89ddc32729463129 differ
 
run.py CHANGED
@@ -4,4 +4,4 @@ os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'
4
  app = create_app()
5
 
6
  if __name__ == '__main__':
7
- app.run(host="0.0.0.0",debug=False, port=7860)
 
4
  app = create_app()
5
 
6
  if __name__ == '__main__':
7
+ app.run(host="0.0.0.0",debug=True, port=7860)