Spaces:
Paused
Paused
WaysAhead Global commited on
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,162 +1,142 @@
|
|
| 1 |
import streamlit as st
|
| 2 |
import pandas as pd
|
|
|
|
| 3 |
import os
|
| 4 |
import matplotlib.pyplot as plt
|
| 5 |
import seaborn as sns
|
| 6 |
-
from
|
| 7 |
-
from
|
| 8 |
-
|
| 9 |
-
# AI & ML Components
|
| 10 |
from langchain_openai import ChatOpenAI
|
| 11 |
-
from
|
| 12 |
-
from pandasai.llm import OpenAI
|
| 13 |
-
from pycaret.regression import setup as setup_reg, compare_models, predict_model, pull, save_model, load_model
|
| 14 |
|
| 15 |
# ✅ Load Environment Variables
|
| 16 |
load_dotenv()
|
| 17 |
openai_api_key = os.getenv("OPENAI_API_KEY")
|
| 18 |
|
| 19 |
if not openai_api_key:
|
| 20 |
-
st.error("❌ OpenAI API Key is missing! Set it in your
|
| 21 |
|
| 22 |
-
# ✅
|
| 23 |
-
|
| 24 |
|
| 25 |
-
#
|
| 26 |
-
|
|
|
|
| 27 |
|
| 28 |
-
#
|
| 29 |
-
repo_path = os.getcwd()
|
| 30 |
-
kpi_file = os.path.join(repo_path, "kpi.xlsx")
|
| 31 |
competitor_files = {
|
| 32 |
-
"
|
| 33 |
-
"
|
| 34 |
-
"
|
| 35 |
-
"
|
| 36 |
-
"
|
| 37 |
}
|
| 38 |
|
| 39 |
-
#
|
| 40 |
-
|
|
|
|
|
|
|
| 41 |
|
| 42 |
-
# ✅
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
try:
|
| 48 |
-
df_competitors[mall] = pd.read_csv(file_path, encoding="ISO-8859-1", on_bad_lines="skip")
|
| 49 |
-
except Exception as e:
|
| 50 |
-
st.error(f"❌ Error loading {file_name}: {e}")
|
| 51 |
|
| 52 |
-
# ✅ Display
|
| 53 |
-
st.subheader("📊
|
| 54 |
-
st.dataframe(
|
| 55 |
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
st.write(f"### {mall} - Competitors")
|
| 59 |
-
st.dataframe(df.head(5))
|
| 60 |
|
| 61 |
-
#
|
| 62 |
-
st.
|
| 63 |
-
selected_mall = st.selectbox("🏬 Select a Mall for Competitor Insights:", df_kpi["Mall"].unique())
|
| 64 |
|
| 65 |
if selected_mall:
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
|
|
|
| 72 |
llm = ChatOpenAI(model="gpt-4", api_key=openai_api_key)
|
| 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 |
-
st.warning(f"⚠️ No reference data available to predict sales for {selected_brand}.")
|
| 139 |
-
|
| 140 |
-
# ✅ AI Chatbot for Business Insights
|
| 141 |
-
st.subheader("💬 AI Chatbot for Business Insights")
|
| 142 |
-
sdl = SmartDatalake([df_kpi, *df_competitors.values()], config={
|
| 143 |
-
"llm": OpenAI(api_token=openai_api_key),
|
| 144 |
-
"use_error_correction_framework": True,
|
| 145 |
-
"enable_cache": False,
|
| 146 |
-
"enable_restricted_mode": False,
|
| 147 |
-
"save_charts": True,
|
| 148 |
-
"save_charts_path": "/home/user/app/exports/charts/temp_chart.png"
|
| 149 |
-
})
|
| 150 |
-
|
| 151 |
-
user_query = st.text_input("🔍 Ask AI About the Dataset:")
|
| 152 |
if user_query:
|
| 153 |
-
with st.spinner("🤖 AI is
|
| 154 |
try:
|
| 155 |
-
|
| 156 |
-
|
|
|
|
|
|
|
| 157 |
except Exception as e:
|
| 158 |
st.error(f"❌ AI Error: {str(e)}")
|
| 159 |
|
| 160 |
# ✅ Footer
|
| 161 |
st.markdown("---")
|
| 162 |
-
st.markdown("💡 Built with ❤️ using **
|
|
|
|
| 1 |
import streamlit as st
|
| 2 |
import pandas as pd
|
| 3 |
+
import numpy as np
|
| 4 |
import os
|
| 5 |
import matplotlib.pyplot as plt
|
| 6 |
import seaborn as sns
|
| 7 |
+
from sklearn.cluster import KMeans
|
| 8 |
+
from statsmodels.tsa.holtwinters import ExponentialSmoothing
|
|
|
|
|
|
|
| 9 |
from langchain_openai import ChatOpenAI
|
| 10 |
+
from dotenv import load_dotenv
|
|
|
|
|
|
|
| 11 |
|
| 12 |
# ✅ Load Environment Variables
|
| 13 |
load_dotenv()
|
| 14 |
openai_api_key = os.getenv("OPENAI_API_KEY")
|
| 15 |
|
| 16 |
if not openai_api_key:
|
| 17 |
+
st.error("❌ OpenAI API Key is missing! Set it in your environment variables.")
|
| 18 |
|
| 19 |
+
# ✅ Auto-Load All Files from the Folder
|
| 20 |
+
folder_path = os.getcwd() # Uses current folder
|
| 21 |
|
| 22 |
+
# Load KPI Excel File (Main store dataset)
|
| 23 |
+
data_file = os.path.join(folder_path, "kpi.xlsx")
|
| 24 |
+
df = pd.read_excel(data_file, engine="openpyxl")
|
| 25 |
|
| 26 |
+
# Load Competitor Mapping Files
|
|
|
|
|
|
|
| 27 |
competitor_files = {
|
| 28 |
+
"Deira City Centre": os.path.join(folder_path, "Competitor Mapping Doc Final1(DEIRA CITY CENTRE).csv"),
|
| 29 |
+
"Arabian Centre": os.path.join(folder_path, "Competitor Mapping Doc Final1(ARABIAN CENTRE).csv"),
|
| 30 |
+
"Al Ghurair Centre": os.path.join(folder_path, "Competitor Mapping Doc Final1(AL GHURAIR CENTRE).csv"),
|
| 31 |
+
"Ibn Battuta Mall": os.path.join(folder_path, "Competitor Mapping Doc Final1(IBN BATTUTA MALL).csv"),
|
| 32 |
+
"Dubai Hills": os.path.join(folder_path, "Competitor Mapping Doc Final1(DUBAI HILLS).csv"),
|
| 33 |
}
|
| 34 |
|
| 35 |
+
# Store all competitor data in a dictionary
|
| 36 |
+
competitor_data = {}
|
| 37 |
+
for mall, file in competitor_files.items():
|
| 38 |
+
competitor_data[mall] = pd.read_csv(file, encoding="ISO-8859-1")
|
| 39 |
|
| 40 |
+
# ✅ Streamlit Page Configuration
|
| 41 |
+
st.set_page_config(page_title="AI Store Replacement, Demand Prediction & Chatbot", layout="wide")
|
| 42 |
+
|
| 43 |
+
# ✅ Title
|
| 44 |
+
st.title("🔄 AI-Powered Store Replacement, Demand Prediction & Chatbot")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
|
| 46 |
+
# ✅ Display Data Preview (5 Random Samples)
|
| 47 |
+
st.subheader("📊 Sample Data Preview from KPI Sheet")
|
| 48 |
+
st.dataframe(df.sample(5)) # Show 5 random unique rows
|
| 49 |
|
| 50 |
+
# ✅ Store Replacement System
|
| 51 |
+
st.subheader("🏬 AI-Based Store Replacement")
|
|
|
|
|
|
|
| 52 |
|
| 53 |
+
# Select Mall
|
| 54 |
+
selected_mall = st.selectbox("🏬 Select a Mall:", df["Mall"].unique())
|
|
|
|
| 55 |
|
| 56 |
if selected_mall:
|
| 57 |
+
# Get all stores in the selected mall
|
| 58 |
+
mall_data = df[df["Mall"] == selected_mall]
|
| 59 |
+
|
| 60 |
+
# Select the store that will be replaced
|
| 61 |
+
store_to_replace = st.selectbox("🚪 Select the store that is closing:", mall_data["Store_Brand"].unique())
|
| 62 |
+
|
| 63 |
+
if store_to_replace:
|
| 64 |
llm = ChatOpenAI(model="gpt-4", api_key=openai_api_key)
|
| 65 |
+
|
| 66 |
+
# ✅ Identify Store Category
|
| 67 |
+
category_prompt = f"Given that {store_to_replace} is a retail brand, what type of store is it? Categorize it broadly (e.g., Fashion, Electronics, Footwear, Food, Home Decor, etc.)."
|
| 68 |
+
store_category = llm.invoke(category_prompt).content
|
| 69 |
+
st.write(f"🔍 **Identified Category:** {store_category}")
|
| 70 |
+
|
| 71 |
+
# ✅ Find 5 Competitors in the Same Mall
|
| 72 |
+
if selected_mall in competitor_data:
|
| 73 |
+
mall_competitor_data = competitor_data[selected_mall]
|
| 74 |
+
competitor_list = mall_competitor_data["Competitors in mall"].dropna().unique()
|
| 75 |
+
|
| 76 |
+
if len(competitor_list) > 5:
|
| 77 |
+
competitor_list = np.random.choice(competitor_list, 5, replace=False)
|
| 78 |
+
|
| 79 |
+
st.subheader("🔎 Competitors in This Mall")
|
| 80 |
+
for competitor in competitor_list:
|
| 81 |
+
st.write(f"- {competitor}")
|
| 82 |
+
|
| 83 |
+
# ✅ AI-Based Store Clustering (Finding Best Replacements)
|
| 84 |
+
store_features = df[["Annual_Sales", "Footfall", "Gross_Margin"]].dropna()
|
| 85 |
+
kmeans = KMeans(n_clusters=5, random_state=42) # 5 Clusters
|
| 86 |
+
df["Cluster"] = kmeans.fit_predict(store_features)
|
| 87 |
+
|
| 88 |
+
# ✅ Find a replacement store in the same category (From kpi.xlsx, Not in this mall)
|
| 89 |
+
all_stores = set(df["Store_Brand"].unique())
|
| 90 |
+
existing_stores = set(mall_data["Store_Brand"].unique())
|
| 91 |
+
potential_replacements = list(all_stores - existing_stores)
|
| 92 |
+
|
| 93 |
+
if potential_replacements:
|
| 94 |
+
replacement_prompt = f"Based on the category {store_category}, suggest 3 stores from the list below that are not already in {selected_mall}. The stores should match the mall’s customer base. Store List: {potential_replacements}"
|
| 95 |
+
replacement_response = llm.invoke(replacement_prompt).content
|
| 96 |
+
replacement_stores = replacement_response.split(", ")
|
| 97 |
+
|
| 98 |
+
st.success(f"🔄 **Recommended Replacement Stores:**")
|
| 99 |
+
for replacement_store in replacement_stores:
|
| 100 |
+
store_data = df[df["Store_Brand"] == replacement_store]
|
| 101 |
+
|
| 102 |
+
if not store_data.empty:
|
| 103 |
+
sales_history = store_data["Annual_Sales"]
|
| 104 |
+
if len(sales_history) > 3:
|
| 105 |
+
# ✅ Time-Series Forecasting for Demand Prediction
|
| 106 |
+
model = ExponentialSmoothing(sales_history, trend="add", seasonal="add", seasonal_periods=3)
|
| 107 |
+
fit = model.fit()
|
| 108 |
+
forecast_sales = fit.forecast(1).values[0] # Predict next year's sales
|
| 109 |
+
|
| 110 |
+
expected_footfall = store_data["Footfall"].mean()
|
| 111 |
+
expected_conversion = store_data["Conversion_Rate"].mean()
|
| 112 |
+
expected_margin = store_data["Gross_Margin"].mean()
|
| 113 |
+
|
| 114 |
+
st.write(f"**{replacement_store}**")
|
| 115 |
+
st.write(f"📊 **Projected Sales:** AED {forecast_sales:,.2f}")
|
| 116 |
+
st.write(f"👣 **Projected Footfall:** {expected_footfall:,.0f} visitors")
|
| 117 |
+
st.write(f"💰 **Projected Margin:** AED {expected_margin:,.2f}")
|
| 118 |
+
st.write(f"🔄 **Projected Conversion Rate:** {expected_conversion:.2f}%")
|
| 119 |
+
|
| 120 |
+
# ✅ Justification
|
| 121 |
+
justification_prompt = f"Explain why {replacement_store} is a good replacement for {store_to_replace} in {selected_mall}. Include numeric KPIs: expected sales, footfall, conversion rate, and profit margins."
|
| 122 |
+
justification = llm.invoke(justification_prompt).content
|
| 123 |
+
st.write(f"🤖 **AI Justification:** {justification}")
|
| 124 |
+
|
| 125 |
+
# ✅ AI Chatbot for Dynamic Insights
|
| 126 |
+
st.subheader("💬 AI-Powered Chatbot")
|
| 127 |
+
|
| 128 |
+
# AI Chatbot using LangChain
|
| 129 |
+
user_query = st.text_input("🔍 Ask anything about store performance, competitors, or pricing:")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
if user_query:
|
| 131 |
+
with st.spinner("🤖 AI is thinking..."):
|
| 132 |
try:
|
| 133 |
+
chat_prompt = f"Answer the following retail business question based on your market knowledge: {user_query}"
|
| 134 |
+
chat_response = llm.invoke(chat_prompt).content
|
| 135 |
+
st.markdown(f"🤖 **AI Response:** {chat_response}")
|
| 136 |
+
|
| 137 |
except Exception as e:
|
| 138 |
st.error(f"❌ AI Error: {str(e)}")
|
| 139 |
|
| 140 |
# ✅ Footer
|
| 141 |
st.markdown("---")
|
| 142 |
+
st.markdown("💡 Built with ❤️ using **LangChain AI for Store Replacement & Demand Prediction**")
|