lokesh341's picture
Update menu.py
d8f44d1 verified
raw
history blame
17 kB
from flask import Blueprint, render_template, request, session, jsonify, redirect, url_for
from salesforce import get_salesforce_connection
import os
import re
import logging
from urllib.parse import quote
from datetime import datetime, timedelta
# Initialize logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
menu_blueprint = Blueprint('menu', __name__)
# Initialize Salesforce connection
sf = get_salesforce_connection()
# Constants
STATIC_DIR = os.path.join(os.path.dirname(__file__), '..', 'static')
PLACEHOLDER_VIDEO = 'placeholder.mp4'
PLACEHOLDER_IMAGE = 'placeholder.jpg'
PLACEHOLDER_VIDEO_PATH = os.path.join(STATIC_DIR, PLACEHOLDER_VIDEO)
PLACEHOLDER_IMAGE_PATH = os.path.join(STATIC_DIR, PLACEHOLDER_IMAGE)
SECTION_ORDER = ["Best Sellers", "Starters", "Biryanis", "Curries", "Breads",
"Customized Dish", "Appetizer", "Desserts", "Soft Drinks"]
MAX_BEST_SELLERS = 4
DEFAULT_VIDEO_URL = "https://yourdomain.my.salesforce.com/sfc/servlet.shepherd/version/download/"
# Custom Jinja2 filter
def slugify(text):
"""Convert text to a slug format (lowercase, replace spaces with hyphens)"""
if not text:
return ""
text = text.lower()
text = re.sub(r'[^\w\s-]', '', text) # Remove non-alphanumeric chars
text = re.sub(r'[\s-]+', '-', text) # Replace spaces and multiple hyphens with single hyphen
return text
# Register the filter with the blueprint
@menu_blueprint.app_template_filter('slugify')
def slugify_filter(s):
return slugify(s)
def initialize_placeholders():
"""Create placeholder files if they don't exist"""
try:
if not os.path.exists(PLACEHOLDER_VIDEO_PATH):
with open(PLACEHOLDER_VIDEO_PATH, 'wb') as f:
f.write(b'') # Empty file
logger.info(f"Created placeholder video at {PLACEHOLDER_VIDEO_PATH}")
if not os.path.exists(PLACEHOLDER_IMAGE_PATH):
with open(PLACEHOLDER_IMAGE_PATH, 'wb') as f:
f.write(b'') # Empty file
logger.info(f"Created placeholder image at {PLACEHOLDER_IMAGE_PATH}")
except Exception as e:
logger.error(f"Error creating placeholder files: {str(e)}")
initialize_placeholders()
def get_valid_media_path(media_url=None, media_type='video'):
"""
Get valid media path with placeholder fallback
Args:
media_url: URL or ID from Salesforce
media_type: 'video' or 'image'
Returns:
Valid URL for the media
"""
# Default placeholders
default_path = f"/static/{PLACEHOLDER_VIDEO if media_type == 'video' else PLACEHOLDER_IMAGE}"
if not media_url:
return default_path
# Handle Salesforce File ID (starts with '069')
if media_url.startswith('069'):
return f"{DEFAULT_VIDEO_URL}{media_url}"
# Handle complete URLs
if media_url.startswith(('http://', 'https://')):
return media_url
# Handle relative paths
if media_url.startswith('/'):
return media_url
return default_path
def validate_user_session():
"""Validate and return user session data or redirect to login"""
user_email = session.get('user_email')
user_name = session.get('user_name')
if not user_email:
user_email = request.args.get('email')
user_name = request.args.get('name')
if user_email:
session['user_email'] = user_email
session['user_name'] = user_name
else:
return None, None, redirect(url_for("login"))
first_letter = user_name[0].upper() if user_name else "A"
return user_email, first_letter, None
def get_user_details(sf_connection, email):
"""Fetch user details from Salesforce"""
try:
query = f"""
SELECT Id, Referral__c, Reward_Points__c
FROM Customer_Login__c
WHERE Email__c = '{email}'
"""
result = sf_connection.query(query)
if not result['records']:
return None
return {
'referral_code': result['records'][0].get('Referral__c', 'N/A'),
'reward_points': result['records'][0].get('Reward_Points__c', 0)
}
except Exception as e:
logger.error(f"Error fetching user details: {str(e)}")
return None
def get_cart_count(sf_connection, email):
"""Get cart item count for user"""
try:
query = f"SELECT COUNT() FROM Cart_Item__c WHERE Customer_Email__c = '{email}'"
result = sf_connection.query(query)
return result['totalSize']
except Exception as e:
logger.error(f"Error fetching cart count: {str(e)}")
return 0
def fetch_menu_items(sf_connection, selected_category):
"""Fetch and process menu items from Salesforce"""
try:
# Query for standard menu items
menu_query = """
SELECT Id, Name, Price__c, Description__c, Image1__c, Image2__c,
Veg_NonVeg__c, Section__c, Total_Ordered__c, Video1__c,
Is_Available__c, Spice_Level__c, Preparation_Time__c
FROM Menu_Item__c
WHERE Is_Available__c = true
"""
menu_result = sf_connection.query(menu_query)
food_items = menu_result.get('records', [])
# Query for custom dishes (last 7 days)
custom_dish_query = """
SELECT Id, Name, Price__c, Description__c, Image1__c, Image2__c,
Veg_NonVeg__c, Section__c, Total_Ordered__c, Is_Available__c
FROM Custom_Dish__c
WHERE CreatedDate >= LAST_N_DAYS:7 AND Is_Available__c = true
"""
custom_dish_result = sf_connection.query(custom_dish_query)
custom_dishes = custom_dish_result.get('records', [])
# Process and merge items
all_items = []
for item in food_items + custom_dishes:
processed_item = {
'id': item.get('Id'),
'name': item.get('Name', 'Unnamed Item'),
'price': item.get('Price__c', 0),
'description': item.get('Description__c', 'No description available'),
'image1': get_valid_media_path(item.get('Image1__c'), 'image'),
'image2': get_valid_media_path(item.get('Image2__c'), 'image'),
'video': get_valid_media_path(item.get('Video1__c'), 'video'),
'veg_non_veg': item.get('Veg_NonVeg__c', 'Unknown'),
'section': item.get('Section__c', 'Others'),
'total_ordered': item.get('Total_Ordered__c', 0),
'is_available': item.get('Is_Available__c', True),
'spice_level': item.get('Spice_Level__c', 'Medium'),
'prep_time': item.get('Preparation_Time__c', 20)
}
# Apply category filter
if selected_category == "Veg" and processed_item['veg_non_veg'] not in ["Veg", "both"]:
continue
if selected_category == "Non veg" and processed_item['veg_non_veg'] not in ["Non veg", "both"]:
continue
all_items.append(processed_item)
return all_items
except Exception as e:
logger.error(f"Error fetching menu items: {str(e)}")
return []
def organize_menu_items(items, selected_category):
"""Organize menu items into sections"""
ordered_menu = {section: [] for section in SECTION_ORDER}
added_item_ids = set()
# Process best sellers
best_sellers = sorted(
[item for item in items if item['total_ordered'] > 0],
key=lambda x: x['total_ordered'],
reverse=True
)[:MAX_BEST_SELLERS]
if best_sellers:
ordered_menu["Best Sellers"] = best_sellers
added_item_ids.update(item['id'] for item in best_sellers)
# Organize other sections
for item in items:
if item['id'] in added_item_ids:
continue
section = item['section']
if section not in ordered_menu:
section = "Others" # Fallback section
ordered_menu[section].append(item)
added_item_ids.add(item['id'])
# Remove empty sections
return {section: items for section, items in ordered_menu.items() if items}
@menu_blueprint.route("/menu", methods=["GET"])
def menu():
# Validate session
user_email, first_letter, redirect_response = validate_user_session()
if redirect_response:
return redirect_response
selected_category = request.args.get("category", "All")
try:
# Get user details
user_details = get_user_details(sf, user_email)
if not user_details:
return redirect(url_for('login'))
# Get cart count
cart_item_count = get_cart_count(sf, user_email)
# Fetch and organize menu items
all_items = fetch_menu_items(sf, selected_category)
ordered_menu = organize_menu_items(all_items, selected_category)
# Prepare categories
categories = ["All", "Veg", "Non veg"]
return render_template(
"menu.html",
ordered_menu=ordered_menu,
categories=categories,
selected_category=selected_category,
referral_code=user_details['referral_code'],
reward_points=user_details['reward_points'],
user_name=session.get('user_name'),
first_letter=first_letter,
cart_item_count=cart_item_count
)
except Exception as e:
logger.error(f"Error in menu route: {str(e)}")
# Fallback data
return render_template(
"menu.html",
ordered_menu={},
categories=["All", "Veg", "Non veg"],
selected_category=selected_category,
referral_code='N/A',
reward_points=0,
user_name=session.get('user_name'),
first_letter=first_letter,
cart_item_count=0,
error_message="We're experiencing technical difficulties. Please try again later."
)
@menu_blueprint.route('/api/addons', methods=['GET'])
def get_addons():
try:
item_name = request.args.get('item_name', '').strip()
item_section = request.args.get('item_section', '').strip()
if not item_name or not item_section:
return jsonify({
"success": False,
"error": "Item name and section are required."
}), 400
# Query customization options
query = f"""
SELECT Id, Name, Customization_Type__c, Options__c,
Max_Selections__c, Extra_Charge__c, Extra_Charge_Amount__c,
Is_Active__c
FROM Customization_Options__c
WHERE Section__c = '{item_section}'
AND Is_Active__c = true
ORDER BY Display_Order__c NULLS LAST
"""
result = sf.query(query)
addons = result.get('records', [])
if not addons:
return jsonify({
"success": False,
"error": "No customization options found."
}), 404
# Format response
formatted_addons = []
for addon in addons:
options = addon.get("Options__c", "")
options = [opt.strip() for opt in options.split(",")] if options else []
formatted_addons.append({
"id": addon.get("Id"),
"name": addon.get("Name"),
"type": addon.get("Customization_Type__c", "checkbox"),
"options": options,
"max_selections": addon.get("Max_Selections__c", 1),
"extra_charge": addon.get("Extra_Charge__c", False),
"extra_charge_amount": float(addon.get("Extra_Charge_Amount__c", 0))
})
return jsonify({
"success": True,
"addons": formatted_addons
})
except Exception as e:
logger.error(f"Error in get_addons: {str(e)}")
return jsonify({
"success": False,
"error": "An error occurred while fetching options."
}), 500
@menu_blueprint.route('/cart/add', methods=['POST'])
def add_to_cart():
try:
# Validate session
if 'user_email' not in session:
return jsonify({
"success": False,
"error": "Authentication required."
}), 401
# Validate input
data = request.get_json()
if not data:
return jsonify({
"success": False,
"error": "Invalid JSON data."
}), 400
required_fields = ['itemName', 'itemPrice', 'itemImage', 'section']
for field in required_fields:
if field not in data or not data[field]:
return jsonify({
"success": False,
"error": f"Missing required field: {field}"
}), 400
# Prepare data
item_data = {
'name': data['itemName'].strip(),
'price': float(data['itemPrice']),
'image': data['itemImage'].strip(),
'section': data['section'].strip(),
'category': data.get('category', ''),
'addons': data.get('addons', []),
'instructions': data.get('instructions', '').strip(),
'quantity': int(data.get('quantity', 1)),
'customer_email': session['user_email']
}
# Calculate addons
addons_price = sum(float(addon.get('price', 0)) for addon in item_data['addons'])
addons_string = "; ".join(
f"{addon.get('name', '')} (${addon.get('price', 0):.2f})"
for addon in item_data['addons']
) if item_data['addons'] else "None"
total_price = (item_data['price'] * item_data['quantity']) + addons_price
# Check if item already exists in cart
query = f"""
SELECT Id, Quantity__c, Add_Ons__c, Add_Ons_Price__c, Instructions__c
FROM Cart_Item__c
WHERE Customer_Email__c = '{item_data['customer_email']}'
AND Name = '{item_data['name']}'
LIMIT 1
"""
result = sf.query(query)
existing_items = result.get('records', [])
if existing_items:
# Update existing item
existing_item = existing_items[0]
new_quantity = existing_item['Quantity__c'] + item_data['quantity']
# Merge addons
existing_addons = existing_item.get('Add_Ons__c', "None")
merged_addons = existing_addons if existing_addons == "None" else f"{existing_addons}; {addons_string}"
# Merge instructions
existing_instructions = existing_item.get('Instructions__c', "")
merged_instructions = existing_instructions if not item_data['instructions'] else \
f"{existing_instructions} | {item_data['instructions']}".strip(" | ")
# Update in Salesforce
sf.Cart_Item__c.update(existing_item['Id'], {
"Quantity__c": new_quantity,
"Add_Ons__c": merged_addons,
"Add_Ons_Price__c": addons_price + existing_item.get('Add_Ons_Price__c', 0),
"Instructions__c": merged_instructions,
"Price__c": total_price + existing_item.get('Price__c', 0)
})
else:
# Create new cart item
sf.Cart_Item__c.create({
"Name": item_data['name'],
"Price__c": total_price,
"Base_Price__c": item_data['price'],
"Quantity__c": item_data['quantity'],
"Add_Ons_Price__c": addons_price,
"Add_Ons__c": addons_string,
"Image1__c": item_data['image'],
"Customer_Email__c": item_data['customer_email'],
"Instructions__c": item_data['instructions'],
"Category__c": item_data['category'],
"Section__c": item_data['section']
})
return jsonify({
"success": True,
"message": "Item added to cart successfully."
})
except ValueError as e:
logger.error(f"Value error in add_to_cart: {str(e)}")
return jsonify({
"success": False,
"error": "Invalid price or quantity format."
}), 400
except Exception as e:
logger.error(f"Error in add_to_cart: {str(e)}")
return jsonify({
"success": False,
"error": "An error occurred while adding to cart."
}), 500