simnid commited on
Commit
0fb1b94
·
verified ·
1 Parent(s): 9922101

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. Dockerfile +9 -12
  2. app.py +95 -169
  3. requirements.txt +3 -2
Dockerfile CHANGED
@@ -1,17 +1,14 @@
1
- FROM python:3.10-slim
 
2
 
 
3
  WORKDIR /app
4
 
5
- COPY requirements.txt /app/requirements.txt
6
- RUN pip install --no-cache-dir -r requirements.txt
7
 
8
- # Copy streamlit app
9
- COPY app.py /app/app.py
10
 
11
- # Optional: baked-in backend base (override with env var in Space)
12
- ENV BACKEND_BASE="https://<your-backend-space>.hf.space"
13
-
14
- EXPOSE 7860
15
-
16
- # Start Streamlit (HF Spaces will handle proxy)
17
- CMD ["streamlit", "run", "app.py", "--server.port=7860", "--server.address=0.0.0.0"]
 
1
+ # Use a minimal base image with Python 3.9 installed
2
+ FROM python:3.9-slim
3
 
4
+ # Set the working directory inside the container to /app
5
  WORKDIR /app
6
 
7
+ # Copy all files from the current directory on the host to the container's /app directory
8
+ COPY . .
9
 
10
+ # Install Python dependencies listed in requirements.txt
11
+ RUN pip3 install -r requirements.txt
12
 
13
+ # Define the command to run the Streamlit app on port 7860 and make it accessible externally
14
+ CMD ["streamlit", "run", "app.py", "--server.port=7860", "--server.enableCORS=false", "--server.address=0.0.0.0", "--server.enableXsrfProtection=false"]
 
 
 
 
 
app.py CHANGED
@@ -1,180 +1,106 @@
1
- import os
2
- import json
3
- import time
4
- import requests
5
- import pandas as pd
6
  import streamlit as st
 
 
 
7
 
8
- # -------------------------------
9
- # Config
10
- # -------------------------------
11
- # Prefer st.secrets; fallback to env var; lastly a hardcoded example
12
- BACKEND_BASE = st.secrets.get("BACKEND_BASE") or os.getenv("BACKEND_BASE") or "https://<your-backend-space>.hf.space"
13
- PREDICT_URL = f"{BACKEND_BASE}/predict"
14
- HEALTH_URL = f"{BACKEND_BASE}/health"
15
- FI_URL = f"{BACKEND_BASE}/feature_importance"
16
-
17
- st.set_page_config(page_title="SuperKart Sales Forecast", page_icon="🛒", layout="wide")
18
-
19
- # Maintain a small prediction history
20
- if "pred_history" not in st.session_state:
21
- st.session_state.pred_history = []
22
-
23
- # -------------------------------
24
- # Header
25
- # -------------------------------
26
- st.title("🛒 SuperKart Sales Forecasting App")
27
- st.caption("Forecast product-level sales with store & product attributes. Deployed via Hugging Face Spaces.")
28
-
29
- # Health check
30
- health_col, base_col = st.columns([1, 3])
31
- with health_col:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  try:
33
- r = requests.get(HEALTH_URL, timeout=3)
34
- ok = r.status_code == 200 and r.json().get("status") == "ok"
35
- if ok:
36
- st.success("Backend: Online")
 
 
 
37
  else:
38
- st.warning("Backend: Unhealthy")
 
39
  except Exception as e:
40
- st.error("Backend: Unreachable")
41
- with base_col:
42
- st.code(BACKEND_BASE, language="text")
43
-
44
- st.divider()
45
-
46
- # -------------------------------
47
- # Input Sidebar
48
- # -------------------------------
49
- with st.sidebar:
50
- st.header("Input Parameters")
51
 
52
- st.subheader("Product")
53
- product_id = st.text_input("Product_Id", value="PR123")
54
- product_weight = st.number_input("Product_Weight (kg)", min_value=0.0, value=1.0, step=0.1)
55
- product_sugar = st.selectbox("Product_Sugar_Content", ["Low Sugar", "Regular", "No Sugar"])
56
- allocated_area = st.slider("Product_Allocated_Area (0-1)", min_value=0.0, max_value=1.0, value=0.10, step=0.01)
57
- product_type = st.selectbox(
58
- "Product_Type",
59
- [
60
- "Meat","Snack Foods","Hard Drinks","Dairy","Canned","Soft Drinks","Health and Hygiene",
61
- "Baking Goods","Bread","Breakfast","Frozen Foods","Fruits and Vegetables","Household",
62
- "Seafood","Starchy Foods","Others"
63
- ],
64
- index=11
65
- )
66
- mrp = st.number_input("Product_MRP", min_value=0.0, value=50.0, step=0.5)
67
 
68
- st.subheader("Store")
69
- store_id = st.text_input("Store_Id", value="ST001")
70
- store_year = st.number_input("Store_Establishment_Year", min_value=1950, max_value=2025, value=2015, step=1)
71
- store_size = st.selectbox("Store_Size", ["Low", "Medium", "High"], index=1)
72
- store_city = st.selectbox("Store_Location_City_Type", ["Tier 1", "Tier 2", "Tier 3"], index=1)
73
- store_type = st.selectbox("Store_Type", ["Departmental Store", "Supermarket Type 1", "Supermarket Type 2", "Food Mart"], index=1)
74
 
75
- st.caption("Tip: These fields must match training columns. The backend pipeline handles encoding & scaling.")
76
-
77
- # -------------------------------
78
- # Payload builder & Validators
79
- # -------------------------------
80
- def build_payload():
81
- return {
82
- "Product_Id": product_id.strip(),
83
- "Product_Weight": float(product_weight),
84
- "Product_Sugar_Content": product_sugar,
85
- "Product_Allocated_Area": float(allocated_area),
86
- "Product_Type": product_type,
87
- "Product_MRP": float(mrp),
88
- "Store_Id": store_id.strip(),
89
- "Store_Establishment_Year": int(store_year),
90
- "Store_Size": store_size,
91
- "Store_Location_City_Type": store_city,
92
- "Store_Type": store_type
93
- }
94
-
95
- def validate_payload(payload: dict) -> list:
96
- errs = []
97
- if not payload["Product_Id"]:
98
- errs.append("Product_Id is required.")
99
- if not payload["Store_Id"]:
100
- errs.append("Store_Id is required.")
101
- if not (0.0 <= payload["Product_Allocated_Area"] <= 1.0):
102
- errs.append("Product_Allocated_Area must be in [0, 1].")
103
- if payload["Product_MRP"] <= 0:
104
- errs.append("Product_MRP must be > 0.")
105
- if payload["Product_Weight"] < 0:
106
- errs.append("Product_Weight must be ≥ 0.")
107
- if not (1950 <= payload["Store_Establishment_Year"] <= 2025):
108
- errs.append("Store_Establishment_Year out of range.")
109
- return errs
110
-
111
- # -------------------------------
112
- # Main Layout
113
- # -------------------------------
114
- left, right = st.columns([1.1, 1])
115
-
116
- with left:
117
- st.subheader("Prediction")
118
- payload = build_payload()
119
-
120
- with st.expander("Show request JSON", expanded=False):
121
- st.json(payload)
122
-
123
- predict_btn = st.button("🔮 Predict Sales", type="primary", use_container_width=True)
124
-
125
- if predict_btn:
126
- errors = validate_payload(payload)
127
- if errors:
128
- for e in errors:
129
- st.error(e)
130
- else:
131
- t0 = time.time()
132
- try:
133
- res = requests.post(PREDICT_URL, json=payload, timeout=10)
134
- if res.status_code == 200:
135
- pred = res.json().get("predicted_sales")
136
- dur = (time.time() - t0) * 1000
137
- st.metric("Predicted Sales Revenue", f"{pred:,.2f}", help=f"Latency: {dur:.0f} ms")
138
- # Record history
139
- row = payload.copy()
140
- row["Prediction"] = pred
141
- row["Latency_ms"] = round(dur, 0)
142
- st.session_state.pred_history.append(row)
143
- else:
144
- st.error(f"Prediction failed ({res.status_code}). Details: {res.text}")
145
- except Exception as ex:
146
- st.exception(ex)
147
-
148
- if st.session_state.pred_history:
149
- st.markdown("### Recent Predictions")
150
- df_history = pd.DataFrame(st.session_state.pred_history)[
151
- ["Product_Id","Store_Id","Product_Type","Store_Type","Product_Allocated_Area","Product_MRP","Prediction","Latency_ms"]
152
- ]
153
- st.dataframe(df_history, use_container_width=True, hide_index=True)
154
-
155
- with right:
156
- st.subheader("Model Insights")
157
- show_importance = st.toggle("Show Feature Importance (top 20)", value=False)
158
-
159
- if show_importance:
160
  try:
161
- r = requests.get(FI_URL, timeout=5)
162
- if r.status_code == 200:
163
- imp = r.json() # dict: feature -> importance
164
- if isinstance(imp, dict) and len(imp) > 0:
165
- imp_df = pd.DataFrame(
166
- sorted(imp.items(), key=lambda x: x[1], reverse=True)[:20],
167
- columns=["Feature","Importance"]
168
- )
169
- st.bar_chart(imp_df.set_index("Feature"))
170
- with st.expander("Raw importance"):
171
- st.dataframe(imp_df, use_container_width=True, hide_index=True)
172
- else:
173
- st.info("Feature importance not available for this model.")
 
 
 
 
 
 
 
174
  else:
175
- st.warning("Feature importance endpoint unavailable.")
176
  except Exception as e:
177
- st.error(f"Could not fetch importance: {e}")
178
-
179
- st.divider()
180
- st.caption("Note: Pipeline performs encoding & scaling internally to avoid leakage and ensure consistency.")
 
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
+ import pandas as pd
3
+ import requests
4
+ import time
5
 
6
+ # Set the title of the Streamlit app
7
+ st.set_page_config(page_title="SuperKart Sales Forecast", layout="centered")
8
+ st.title("SuperKart Sales Prediction")
9
+
10
+ # Health check endpoint for Hugging Face
11
+ if st.query_params.get("healthcheck") == "true":
12
+ st.write("OK")
13
+ st.stop()
14
+
15
+ # Section for single prediction
16
+ st.subheader("Single Product-Store Prediction")
17
+
18
+ # Collect user input for product and store features
19
+ product_weight = st.number_input("Product Weight", min_value=0.1, max_value=20.0, value=12.65, step=0.1)
20
+ product_allocated_area = st.number_input("Product Allocated Area Ratio", min_value=0.0, max_value=1.0, value=0.07, step=0.01)
21
+ product_mrp = st.number_input("Product MRP", min_value=10, max_value=200, value=147, step=1)
22
+ product_sugar_content = st.selectbox("Product Sugar Content", ["Low Sugar", "Regular", "No Sugar"])
23
+ product_type = st.selectbox("Product Type", [
24
+ "Fruits and Vegetables", "Household", "Snack Foods", "Meat",
25
+ "Hard Drinks", "Dairy", "Canned", "Soft Drinks",
26
+ "Health and Hygiene", "Baking Goods", "Bread", "Breakfast",
27
+ "Frozen Foods", "Seafood", "Starchy Foods", "Others"
28
+ ])
29
+ store_size = st.selectbox("Store Size", ["High", "Medium", "Low"])
30
+ store_location_city_type = st.selectbox("Store Location City Type", ["Tier 1", "Tier 2", "Tier 3"])
31
+ store_type = st.selectbox("Store Type", ["Departmental Store", "Supermarket Type 1", "Supermarket Type 2", "Food Mart"])
32
+ store_age = st.number_input("Store Age (years)", min_value=1, max_value=50, value=10, step=1)
33
+ product_category_code = st.number_input("Product Category Code", min_value=1, max_value=100, value=10, step=1)
34
+
35
+ # Convert user input into the format expected by the API
36
+ input_data = {
37
+ 'Product_Weight': float(product_weight),
38
+ 'Product_Allocated_Area': float(product_allocated_area),
39
+ 'Product_MRP': int(product_mrp),
40
+ 'Product_Sugar_Content': product_sugar_content,
41
+ 'Product_Type': product_type,
42
+ 'Store_Size': store_size,
43
+ 'Store_Location_City_Type': store_location_city_type,
44
+ 'Store_Type': store_type,
45
+ 'Store_Age': int(store_age),
46
+ 'Product_Category_Code': int(product_category_code)
47
+ }
48
+
49
+ # Make prediction when the "Predict" button is clicked
50
+ if st.button("Predict Sales"):
51
  try:
52
+ # Replace with your actual backend URL
53
+ backend_url = "https://simnid-superkartsalesbackend.hf.space/v1/predict"
54
+ response = requests.post(backend_url, json=input_data)
55
+
56
+ if response.status_code == 200:
57
+ prediction = response.json()['Predicted Sales Total']
58
+ st.success(f"Predicted Sales Total: ${prediction:,.2f}")
59
  else:
60
+ st.error(f"Error making prediction. Status code: {response.status_code}")
61
+ st.write(response.text)
62
  except Exception as e:
63
+ st.error(f"Error connecting to API: {e}")
 
 
 
 
 
 
 
 
 
 
64
 
65
+ # Section for batch prediction
66
+ st.subheader("Batch Prediction")
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
+ # Allow users to upload a CSV file for batch prediction
69
+ uploaded_file = st.file_uploader("Upload CSV file for batch prediction", type=["csv"])
 
 
 
 
70
 
71
+ # Make batch prediction when the "Predict Batch" button is clicked
72
+ if uploaded_file is not None:
73
+ if st.button("Predict Batch"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  try:
75
+ # Replace with your actual backend batch URL
76
+ batch_backend_url = "https://simnid-superkartsalesbackend.hf.space/v1/predictbatch"
77
+ response = requests.post(batch_backend_url, files={"file": uploaded_file})
78
+
79
+ if response.status_code == 200:
80
+ predictions = response.json()
81
+ st.success("Batch predictions completed!")
82
+
83
+ # Display predictions in a nice format
84
+ predictions_df = pd.DataFrame.from_dict(predictions, orient='index', columns=['Predicted Sales'])
85
+ st.dataframe(predictions_df)
86
+
87
+ # Add download button
88
+ csv = predictions_df.to_csv(index=True)
89
+ st.download_button(
90
+ label="Download Predictions as CSV",
91
+ data=csv,
92
+ file_name="superkart_predictions.csv",
93
+ mime="text/csv"
94
+ )
95
  else:
96
+ st.error(f"Error making batch prediction. Status code: {response.status_code}")
97
  except Exception as e:
98
+ st.error(f"Error connecting to API: {e}")
99
+
100
+ # Add some information about the app
101
+ st.sidebar.header("About")
102
+ st.sidebar.info("""
103
+ This app predicts sales totals for SuperKart products using a machine learning model.
104
+ - **Single Prediction**: Enter details for one product-store combination
105
+ - **Batch Prediction**: Upload a CSV file with multiple records
106
+ """)
requirements.txt CHANGED
@@ -1,3 +1,4 @@
1
- streamlit==1.37.0
2
  pandas==2.2.2
3
- requests==2.32.3
 
 
 
 
1
  pandas==2.2.2
2
+ requests==2.28.1
3
+ streamlit==1.43.2
4
+ numpy==2.0.2