itsjarvis commited on
Commit
061bcee
ยท
verified ยท
1 Parent(s): 0a5436a

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +696 -310
app.py CHANGED
@@ -1,337 +1,723 @@
1
- from flask import Flask, request, jsonify
2
- import joblib
3
- import numpy as np
4
  import pandas as pd
5
- from flask_cors import CORS
6
- import logging
7
  from datetime import datetime
8
- import os
9
- import traceback
10
 
11
- # Initialize Flask app
12
- app = Flask(__name__)
13
- CORS(app) # Enable CORS for all routes
14
-
15
- # Configure logging
16
- logging.basicConfig(
17
- level=logging.INFO,
18
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
19
  )
20
- logger = logging.getLogger(__name__)
21
 
22
- # Global variables for model and preprocessor
23
- model = None
24
- preprocessor = None
25
- model_artifacts = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
- def load_model():
28
- """Load the trained model and preprocessing artifacts."""
29
- global model, preprocessor, model_artifacts
30
-
 
31
  try:
32
- model_path = 'superkart_sales_forecasting_model.joblib'
33
-
34
- if not os.path.exists(model_path):
35
- logger.error(f"Model file not found: {model_path}")
36
- return False
37
-
38
- # Load model artifacts
39
- model_artifacts = joblib.load(model_path)
40
- model = model_artifacts['model']
41
- preprocessor = model_artifacts['preprocessor']
42
-
43
- logger.info(f"Model loaded successfully: {model_artifacts['model_name']}")
44
- logger.info(f"Training date: {model_artifacts['training_date']}")
45
-
46
- return True
47
-
48
- except Exception as e:
49
- logger.error(f"Error loading model: {str(e)}")
50
  return False
51
 
52
- def validate_input_data(data):
53
- """Validate input data for prediction."""
54
- required_fields = [
55
- 'Product_Weight', 'Product_Sugar_Content', 'Product_Allocated_Area',
56
- 'Product_Type', 'Product_MRP', 'Store_Size',
57
- 'Store_Location_City_Type', 'Store_Type', 'Store_Age'
58
- ]
59
-
60
- # Check if all required fields are present
61
- missing_fields = [field for field in required_fields if field not in data]
62
- if missing_fields:
63
- return False, f"Missing required fields: {missing_fields}"
64
-
65
- # Validate data types and ranges
66
  try:
67
- # Numerical validations
68
- if not isinstance(data['Product_Weight'], (int, float)) or data['Product_Weight'] <= 0:
69
- return False, "Product_Weight must be a positive number"
70
-
71
- if not isinstance(data['Product_Allocated_Area'], (int, float)) or not (0 <= data['Product_Allocated_Area'] <= 1):
72
- return False, "Product_Allocated_Area must be between 0 and 1"
73
-
74
- if not isinstance(data['Product_MRP'], (int, float)) or data['Product_MRP'] <= 0:
75
- return False, "Product_MRP must be a positive number"
76
-
77
- if not isinstance(data['Store_Age'], (int, float)) or data['Store_Age'] < 0:
78
- return False, "Store_Age must be a non-negative number"
79
-
80
- # Categorical validations
81
- valid_sugar_content = ['Low Sugar', 'Regular', 'No Sugar']
82
- if data['Product_Sugar_Content'] not in valid_sugar_content:
83
- return False, f"Product_Sugar_Content must be one of: {valid_sugar_content}"
84
-
85
- valid_store_sizes = ['Small', 'Medium', 'High']
86
- if data['Store_Size'] not in valid_store_sizes:
87
- return False, f"Store_Size must be one of: {valid_store_sizes}"
88
-
89
- valid_city_types = ['Tier 1', 'Tier 2', 'Tier 3']
90
- if data['Store_Location_City_Type'] not in valid_city_types:
91
- return False, f"Store_Location_City_Type must be one of: {valid_city_types}"
92
-
93
- valid_store_types = ['Departmental Store', 'Supermarket Type1', 'Supermarket Type2', 'Food Mart']
94
- if data['Store_Type'] not in valid_store_types:
95
- return False, f"Store_Type must be one of: {valid_store_types}"
96
-
97
- return True, "Validation passed"
98
-
99
  except Exception as e:
100
- return False, f"Validation error: {str(e)}"
101
 
102
- def preprocess_for_prediction(data):
103
- """Preprocess input data for model prediction."""
104
  try:
105
- # Convert to DataFrame
106
- if isinstance(data, dict):
107
- df = pd.DataFrame([data])
 
 
 
 
 
 
108
  else:
109
- df = pd.DataFrame(data)
110
-
111
- # Feature engineering functions (must match training)
112
- def categorize_mrp(mrp):
113
- if mrp <= 69.0:
114
- return 'Low'
115
- elif mrp <= 136.0:
116
- return 'Medium_Low'
117
- elif mrp <= 202.0:
118
- return 'Medium_High'
119
- else:
120
- return 'High'
121
-
122
- def categorize_weight(weight):
123
- if weight <= 8.773:
124
- return 'Light'
125
- elif weight <= 12.89:
126
- return 'Medium_Light'
127
- elif weight <= 16.95:
128
- return 'Medium_Heavy'
129
- else:
130
- return 'Heavy'
131
-
132
- def categorize_store_age(age):
133
- if age <= 20:
134
- return 'New'
135
- elif age <= 30:
136
- return 'Established'
137
- else:
138
- return 'Legacy'
139
-
140
- # Add engineered features
141
- df['Product_MRP_Category'] = df['Product_MRP'].apply(categorize_mrp)
142
- df['Product_Weight_Category'] = df['Product_Weight'].apply(categorize_weight)
143
- df['Store_Age_Category'] = df['Store_Age'].apply(categorize_store_age)
144
- df['City_Store_Type'] = df['Store_Location_City_Type'] + '_' + df['Store_Type']
145
- df['Size_Type_Interaction'] = df['Store_Size'] + '_' + df['Store_Type']
146
-
147
- # Transform using the preprocessing pipeline
148
- processed_data = preprocessor.transform(df)
149
-
150
- return processed_data, None
151
-
152
  except Exception as e:
153
- return None, str(e)
154
 
155
- @app.route('/', methods=['GET'])
156
- def home():
157
- """Home endpoint with API information."""
158
- api_info = {
159
- "message": "SuperKart Sales Forecasting API",
160
- "version": "1.0",
161
- "model_info": {
162
- "name": model_artifacts['model_name'] if model_artifacts else "Model not loaded",
163
- "training_date": model_artifacts['training_date'] if model_artifacts else "Unknown",
164
- "version": model_artifacts['model_version'] if model_artifacts else "Unknown"
165
- } if model_artifacts else {"status": "Model not loaded"},
166
- "endpoints": {
167
- "/": "API information",
168
- "/health": "Health check",
169
- "/predict": "Single prediction (POST)",
170
- "/batch_predict": "Batch predictions (POST)",
171
- "/model_info": "Model details"
172
- },
173
- "sample_input": {
174
- "Product_Weight": 10.5,
175
- "Product_Sugar_Content": "Low Sugar",
176
- "Product_Allocated_Area": 0.15,
177
- "Product_Type": "Fruits and Vegetables",
178
- "Product_MRP": 150.0,
179
- "Store_Size": "Medium",
180
- "Store_Location_City_Type": "Tier 2",
181
- "Store_Type": "Supermarket Type2",
182
- "Store_Age": 15
183
- }
184
- }
185
- return jsonify(api_info)
186
-
187
- @app.route('/health', methods=['GET'])
188
- def health_check():
189
- """Health check endpoint."""
190
- health_status = {
191
- "status": "healthy" if model is not None else "unhealthy",
192
- "model_loaded": model is not None,
193
- "timestamp": datetime.now().isoformat(),
194
- "service": "SuperKart Sales Forecasting API"
195
- }
196
- return jsonify(health_status)
197
-
198
- @app.route('/model_info', methods=['GET'])
199
- def model_info():
200
- """Get detailed model information."""
201
- if model_artifacts is None:
202
- return jsonify({"error": "Model not loaded"}), 500
203
-
204
- info = {
205
- "model_name": model_artifacts['model_name'],
206
- "training_date": model_artifacts['training_date'],
207
- "model_version": model_artifacts['model_version'],
208
- "performance_metrics": model_artifacts['performance_metrics'],
209
- "feature_count": len(model_artifacts['feature_names']),
210
- "model_type": type(model).__name__
211
- }
212
 
213
- return jsonify(info)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
 
215
- @app.route('/predict', methods=['POST'])
216
- def predict():
217
- """Single prediction endpoint."""
218
- try:
219
- # Get JSON data from request
220
- data = request.get_json()
221
-
222
- if data is None:
223
- return jsonify({"error": "No JSON data provided"}), 400
224
-
225
- # Validate input data
226
- is_valid, validation_message = validate_input_data(data)
227
- if not is_valid:
228
- return jsonify({"error": validation_message}), 400
229
-
230
- # Preprocess data
231
- processed_data, error = preprocess_for_prediction(data)
232
- if error:
233
- return jsonify({"error": f"Preprocessing failed: {error}"}), 400
234
-
235
- # Make prediction
236
- prediction = model.predict(processed_data)[0]
237
-
238
- # Prepare response
239
- response = {
240
- "prediction": float(prediction),
241
- "input_data": data,
242
- "model_info": {
243
- "model_name": model_artifacts['model_name'],
244
- "prediction_timestamp": datetime.now().isoformat()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
  }
246
- }
247
-
248
- logger.info(f"Prediction made: {prediction:.2f}")
249
- return jsonify(response)
250
-
251
- except Exception as e:
252
- logger.error(f"Prediction error: {str(e)}")
253
- logger.error(f"Traceback: {traceback.format_exc()}")
254
- return jsonify({"error": f"Prediction failed: {str(e)}"}), 500
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
255
 
256
- @app.route('/batch_predict', methods=['POST'])
257
- def batch_predict():
258
- """Batch prediction endpoint."""
259
- try:
260
- # Get JSON data from request
261
- data = request.get_json()
262
-
263
- if data is None:
264
- return jsonify({"error": "No JSON data provided"}), 400
265
-
266
- # Ensure data is a list
267
- if not isinstance(data, list):
268
- return jsonify({"error": "Data must be a list of records"}), 400
269
-
270
- if len(data) == 0:
271
- return jsonify({"error": "Empty data list provided"}), 400
272
-
273
- predictions = []
274
- errors = []
275
-
276
- for i, record in enumerate(data):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
  try:
278
- # Validate input data
279
- is_valid, validation_message = validate_input_data(record)
280
- if not is_valid:
281
- errors.append(f"Record {i}: {validation_message}")
282
- predictions.append(None)
283
- continue
 
284
 
285
- # Preprocess data
286
- processed_data, error = preprocess_for_prediction(record)
287
- if error:
288
- errors.append(f"Record {i}: Preprocessing failed - {error}")
289
- predictions.append(None)
290
- continue
291
 
292
- # Make prediction
293
- prediction = model.predict(processed_data)[0]
294
- predictions.append(float(prediction))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
295
 
296
  except Exception as e:
297
- errors.append(f"Record {i}: {str(e)}")
298
- predictions.append(None)
299
-
300
- # Prepare response
301
- response = {
302
- "predictions": predictions,
303
- "total_records": len(data),
304
- "successful_predictions": len([p for p in predictions if p is not None]),
305
- "errors": errors if errors else None,
306
- "model_info": {
307
- "model_name": model_artifacts['model_name'],
308
- "prediction_timestamp": datetime.now().isoformat()
309
- }
310
- }
311
-
312
- logger.info(f"Batch prediction completed: {len(predictions)} records processed")
313
- return jsonify(response)
314
-
315
- except Exception as e:
316
- logger.error(f"Batch prediction error: {str(e)}")
317
- return jsonify({"error": f"Batch prediction failed: {str(e)}"}), 500
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
318
 
319
- # Initialize the model when the app starts
320
- @app.before_first_request
321
- def initialize():
322
- """Initialize the model on first request."""
323
- logger.info("Initializing SuperKart Sales Forecasting API...")
324
- success = load_model()
325
- if success:
326
- logger.info("API initialization completed successfully")
327
- else:
328
- logger.error("API initialization failed - model could not be loaded")
329
 
330
- if __name__ == '__main__':
331
- # Load model
332
- if load_model():
333
- print("[SUCCESS] Model loaded successfully")
334
- print("[STARTING] SuperKart Sales Forecasting API...")
335
- app.run(host='0.0.0.0', port=8080, debug=False)
336
- else:
337
- print("[ERROR] Failed to load model. Please check model file.")
 
1
+ import streamlit as st
2
+ import requests
3
+ import json
4
  import pandas as pd
5
+ import plotly.express as px
6
+ import plotly.graph_objects as go
7
  from datetime import datetime
8
+ import time
 
9
 
10
+ # Page configuration
11
+ st.set_page_config(
12
+ page_title="SuperKart Sales Forecasting",
13
+ page_icon="๐Ÿ›’",
14
+ layout="wide",
15
+ initial_sidebar_state="expanded"
 
 
16
  )
 
17
 
18
+ # Custom CSS for better styling
19
+ st.markdown("""
20
+ <style>
21
+ .main-header {
22
+ font-size: 3rem;
23
+ color: #1f77b4;
24
+ text-align: center;
25
+ margin-bottom: 2rem;
26
+ }
27
+ .metric-card {
28
+ background-color: #f0f2f6;
29
+ padding: 1rem;
30
+ border-radius: 0.5rem;
31
+ border-left: 4px solid #1f77b4;
32
+ }
33
+ .prediction-result {
34
+ font-size: 2rem;
35
+ font-weight: bold;
36
+ color: #2e8b57;
37
+ text-align: center;
38
+ padding: 1rem;
39
+ background-color: #f0fff0;
40
+ border-radius: 0.5rem;
41
+ border: 2px solid #2e8b57;
42
+ }
43
+ .error-message {
44
+ color: #dc3545;
45
+ background-color: #f8d7da;
46
+ padding: 1rem;
47
+ border-radius: 0.5rem;
48
+ border: 1px solid #f5c6cb;
49
+ }
50
+ </style>
51
+ """, unsafe_allow_html=True)
52
 
53
+ # API Configuration
54
+ API_BASE_URL = "https://your-backend-api-url.hf.space" # Replace with your actual API URL
55
+
56
+ def check_api_health():
57
+ """Check if the API is healthy and accessible."""
58
  try:
59
+ response = requests.get(f"{API_BASE_URL}/health", timeout=10)
60
+ return response.status_code == 200
61
+ except:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  return False
63
 
64
+ def make_prediction(data):
65
+ """Make a single prediction using the API."""
 
 
 
 
 
 
 
 
 
 
 
 
66
  try:
67
+ response = requests.post(
68
+ f"{API_BASE_URL}/predict",
69
+ json=data,
70
+ headers={"Content-Type": "application/json"},
71
+ timeout=30
72
+ )
73
+
74
+ if response.status_code == 200:
75
+ return response.json(), None
76
+ else:
77
+ return None, f"API Error: {response.status_code} - {response.text}"
78
+
79
+ except requests.exceptions.Timeout:
80
+ return None, "Request timeout. Please try again."
81
+ except requests.exceptions.ConnectionError:
82
+ return None, "Cannot connect to API. Please check your internet connection."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  except Exception as e:
84
+ return None, f"Unexpected error: {str(e)}"
85
 
86
+ def make_batch_prediction(data_list):
87
+ """Make batch predictions using the API."""
88
  try:
89
+ response = requests.post(
90
+ f"{API_BASE_URL}/batch_predict",
91
+ json=data_list,
92
+ headers={"Content-Type": "application/json"},
93
+ timeout=60
94
+ )
95
+
96
+ if response.status_code == 200:
97
+ return response.json(), None
98
  else:
99
+ return None, f"API Error: {response.status_code} - {response.text}"
100
+
101
+ except requests.exceptions.Timeout:
102
+ return None, "Request timeout. Please try again."
103
+ except requests.exceptions.ConnectionError:
104
+ return None, "Cannot connect to API. Please check your internet connection."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  except Exception as e:
106
+ return None, f"Unexpected error: {str(e)}"
107
 
108
+ def main():
109
+ """Main Streamlit application."""
110
+
111
+ # Main header
112
+ st.markdown('<h1 class="main-header">๐Ÿ›’ SuperKart Sales Forecasting</h1>', unsafe_allow_html=True)
113
+ st.markdown("### AI-Powered Sales Prediction for Retail Excellence")
114
+
115
+ # Sidebar for navigation
116
+ st.sidebar.title("๐ŸŽฏ Navigation")
117
+ app_mode = st.sidebar.selectbox("Choose the app mode",
118
+ ["Single Prediction", "Batch Prediction", "Analytics Dashboard", "API Status"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
 
120
+ # API Health Check
121
+ with st.sidebar:
122
+ st.markdown("---")
123
+ st.subheader("๐Ÿ”ง API Status")
124
+
125
+ if st.button("Check API Health"):
126
+ with st.spinner("Checking API..."):
127
+ if check_api_health():
128
+ st.success("โœ… API is healthy")
129
+ else:
130
+ st.error("โŒ API is not accessible")
131
+
132
+ st.markdown("---")
133
+ st.markdown("""
134
+ **Features:**
135
+ - ๐ŸŽฏ Single Prediction
136
+ - ๐Ÿ“Š Batch Predictions
137
+ - ๐Ÿ“ˆ Analytics Dashboard
138
+ - ๐Ÿ” Real-time Validation
139
+ """)
140
+
141
+ # Main content based on selected mode
142
+ if app_mode == "Single Prediction":
143
+ single_prediction_page()
144
+ elif app_mode == "Batch Prediction":
145
+ batch_prediction_page()
146
+ elif app_mode == "Analytics Dashboard":
147
+ analytics_dashboard_page()
148
+ elif app_mode == "API Status":
149
+ api_status_page()
150
 
151
+ def single_prediction_page():
152
+ """Single prediction interface."""
153
+
154
+ st.header("๐ŸŽฏ Single Sales Prediction")
155
+ st.markdown("Enter product and store details to get an instant sales forecast.")
156
+
157
+ # Create input form
158
+ with st.form("prediction_form"):
159
+ col1, col2, col3 = st.columns(3)
160
+
161
+ with col1:
162
+ st.subheader("๐Ÿ“ฆ Product Details")
163
+ product_weight = st.number_input(
164
+ "Product Weight (kg)",
165
+ min_value=0.1,
166
+ max_value=50.0,
167
+ value=12.5,
168
+ step=0.1,
169
+ help="Weight of the product in kilograms"
170
+ )
171
+
172
+ product_sugar_content = st.selectbox(
173
+ "Sugar Content",
174
+ ["Low Sugar", "Regular", "No Sugar"],
175
+ help="Sugar content category of the product"
176
+ )
177
+
178
+ product_allocated_area = st.number_input(
179
+ "Allocated Display Area",
180
+ min_value=0.001,
181
+ max_value=1.0,
182
+ value=0.1,
183
+ step=0.001,
184
+ format="%.3f",
185
+ help="Ratio of allocated display area (0-1)"
186
+ )
187
+
188
+ product_type = st.selectbox(
189
+ "Product Type",
190
+ [
191
+ "Fruits and Vegetables", "Snack Foods", "Household", "Frozen Foods",
192
+ "Dairy", "Canned", "Baking Goods", "Health and Hygiene", "Meat",
193
+ "Soft Drinks", "Hard Drinks", "Starchy Foods", "Breakfast",
194
+ "Seafood", "Bread", "Others"
195
+ ],
196
+ help="Category of the product"
197
+ )
198
+
199
+ product_mrp = st.number_input(
200
+ "Maximum Retail Price (โ‚น)",
201
+ min_value=1.0,
202
+ max_value=500.0,
203
+ value=150.0,
204
+ step=1.0,
205
+ help="Maximum retail price in rupees"
206
+ )
207
+
208
+ with col2:
209
+ st.subheader("๐Ÿช Store Details")
210
+ store_size = st.selectbox(
211
+ "Store Size",
212
+ ["Small", "Medium", "High"],
213
+ index=1,
214
+ help="Size category of the store"
215
+ )
216
+
217
+ store_location_city_type = st.selectbox(
218
+ "City Type",
219
+ ["Tier 1", "Tier 2", "Tier 3"],
220
+ index=1,
221
+ help="Tier classification of the city"
222
+ )
223
+
224
+ store_type = st.selectbox(
225
+ "Store Type",
226
+ ["Departmental Store", "Supermarket Type1", "Supermarket Type2", "Food Mart"],
227
+ index=2,
228
+ help="Type/format of the store"
229
+ )
230
+
231
+ store_age = st.number_input(
232
+ "Store Age (years)",
233
+ min_value=0,
234
+ max_value=50,
235
+ value=15,
236
+ step=1,
237
+ help="Age of the store in years"
238
+ )
239
+
240
+ with col3:
241
+ st.subheader("๐Ÿ“Š Prediction Summary")
242
+ st.markdown("""
243
+ **Input Validation:**
244
+ - All fields are required
245
+ - Weights: 0.1 - 50 kg
246
+ - Display Area: 0.001 - 1.0
247
+ - MRP: โ‚น1 - โ‚น500
248
+ - Store Age: 0 - 50 years
249
+ """)
250
+
251
+ st.markdown("---")
252
+ st.markdown("**Business Context:**")
253
+ st.markdown("This prediction helps with:")
254
+ st.markdown("- Inventory planning")
255
+ st.markdown("- Revenue forecasting")
256
+ st.markdown("- Store optimization")
257
+ st.markdown("- Regional strategy")
258
+
259
+ # Submit button
260
+ submitted = st.form_submit_button("๐Ÿš€ Predict Sales", use_container_width=True)
261
+
262
+ if submitted:
263
+ # Prepare data for API
264
+ prediction_data = {
265
+ "Product_Weight": product_weight,
266
+ "Product_Sugar_Content": product_sugar_content,
267
+ "Product_Allocated_Area": product_allocated_area,
268
+ "Product_Type": product_type,
269
+ "Product_MRP": product_mrp,
270
+ "Store_Size": store_size,
271
+ "Store_Location_City_Type": store_location_city_type,
272
+ "Store_Type": store_type,
273
+ "Store_Age": store_age
274
  }
275
+
276
+ # Make prediction
277
+ with st.spinner("๐Ÿ”ฎ Generating prediction..."):
278
+ result, error = make_prediction(prediction_data)
279
+
280
+ if result:
281
+ prediction = result["prediction"]
282
+
283
+ # Display result
284
+ st.success("โœ… Prediction Generated Successfully!")
285
+
286
+ # Main prediction result
287
+ st.markdown(
288
+ f'<div class="prediction-result">๐Ÿ’ฐ Predicted Sales: โ‚น{prediction:,.2f}</div>',
289
+ unsafe_allow_html=True
290
+ )
291
+
292
+ # Additional insights
293
+ col1, col2, col3 = st.columns(3)
294
+
295
+ with col1:
296
+ st.metric(
297
+ "Daily Revenue",
298
+ f"โ‚น{prediction:,.2f}",
299
+ delta=f"{prediction*0.1:,.2f}",
300
+ delta_color="normal"
301
+ )
302
+
303
+ with col2:
304
+ monthly_estimate = prediction * 30
305
+ st.metric(
306
+ "Monthly Estimate",
307
+ f"โ‚น{monthly_estimate:,.2f}",
308
+ delta="Projected",
309
+ delta_color="off"
310
+ )
311
+
312
+ with col3:
313
+ annual_estimate = prediction * 365
314
+ st.metric(
315
+ "Annual Potential",
316
+ f"โ‚น{annual_estimate:,.2f}",
317
+ delta="Estimated",
318
+ delta_color="off"
319
+ )
320
+
321
+ # Business recommendations
322
+ st.markdown("### ๐Ÿ’ก Business Insights")
323
+
324
+ if prediction > 4000:
325
+ st.success("๐ŸŽฏ **High Performance Expected** - This product-store combination shows excellent potential!")
326
+ elif prediction > 2500:
327
+ st.info("๐Ÿ“ˆ **Good Performance Expected** - Solid sales potential with room for optimization.")
328
+ else:
329
+ st.warning("โš ๏ธ **Moderate Performance Expected** - Consider promotional strategies or product mix optimization.")
330
+
331
+ # Performance category analysis
332
+ if store_location_city_type == "Tier 1" and store_type == "Departmental Store":
333
+ st.info("๐Ÿ† **Premium Market Position** - Tier 1 Departmental Store typically shows highest performance.")
334
+
335
+ if product_weight > 15:
336
+ st.info("๐Ÿ“ฆ **Heavy Product Advantage** - Higher weight products tend to generate more sales.")
337
+
338
+ if product_mrp > 200:
339
+ st.info("๐Ÿ’Ž **Premium Product** - High MRP products often indicate better margins.")
340
+
341
+ else:
342
+ st.error(f"โŒ Prediction Failed: {error}")
343
+ st.markdown("""
344
+ **Troubleshooting Steps:**
345
+ 1. Check your internet connection
346
+ 2. Verify API URL in the sidebar
347
+ 3. Ensure all input values are within valid ranges
348
+ 4. Try again in a few moments
349
+ """)
350
 
351
+ def batch_prediction_page():
352
+ """Batch prediction interface."""
353
+
354
+ st.header("๐Ÿ“Š Batch Sales Prediction")
355
+ st.markdown("Upload a CSV file or enter multiple records for bulk predictions.")
356
+
357
+ # Option selection
358
+ batch_option = st.radio(
359
+ "Choose input method:",
360
+ ["Upload CSV File", "Manual Entry"]
361
+ )
362
+
363
+ if batch_option == "Upload CSV File":
364
+ st.subheader("๐Ÿ“ Upload CSV File")
365
+
366
+ # File upload
367
+ uploaded_file = st.file_uploader(
368
+ "Choose a CSV file",
369
+ type="csv",
370
+ help="Upload a CSV file with the required columns"
371
+ )
372
+
373
+ # Show required format
374
+ with st.expander("๐Ÿ“‹ Required CSV Format"):
375
+ sample_df = pd.DataFrame({
376
+ "Product_Weight": [12.5, 16.2, 8.9],
377
+ "Product_Sugar_Content": ["Low Sugar", "Regular", "No Sugar"],
378
+ "Product_Allocated_Area": [0.1, 0.15, 0.05],
379
+ "Product_Type": ["Fruits and Vegetables", "Dairy", "Snack Foods"],
380
+ "Product_MRP": [150.0, 180.0, 95.0],
381
+ "Store_Size": ["Medium", "High", "Small"],
382
+ "Store_Location_City_Type": ["Tier 2", "Tier 1", "Tier 3"],
383
+ "Store_Type": ["Supermarket Type2", "Departmental Store", "Food Mart"],
384
+ "Store_Age": [15, 20, 8]
385
+ })
386
+ st.dataframe(sample_df)
387
+
388
+ if uploaded_file is not None:
389
  try:
390
+ # Read CSV
391
+ df = pd.read_csv(uploaded_file)
392
+ st.success(f"โœ… File uploaded successfully! {len(df)} records found.")
393
+
394
+ # Show preview
395
+ st.subheader("๐Ÿ“‹ Data Preview")
396
+ st.dataframe(df.head())
397
 
398
+ # Validate columns
399
+ required_columns = [
400
+ "Product_Weight", "Product_Sugar_Content", "Product_Allocated_Area",
401
+ "Product_Type", "Product_MRP", "Store_Size",
402
+ "Store_Location_City_Type", "Store_Type", "Store_Age"
403
+ ]
404
 
405
+ missing_columns = [col for col in required_columns if col not in df.columns]
406
+
407
+ if missing_columns:
408
+ st.error(f"โŒ Missing required columns: {missing_columns}")
409
+ else:
410
+ st.success("โœ… All required columns found!")
411
+
412
+ if st.button("๐Ÿš€ Generate Batch Predictions"):
413
+ # Convert DataFrame to list of dictionaries
414
+ data_list = df.to_dict('records')
415
+
416
+ with st.spinner(f"๐Ÿ”ฎ Generating predictions for {len(data_list)} records..."):
417
+ result, error = make_batch_prediction(data_list)
418
+
419
+ if result:
420
+ predictions = result["predictions"]
421
+
422
+ # Add predictions to DataFrame
423
+ df_results = df.copy()
424
+ df_results["Predicted_Sales"] = predictions
425
+
426
+ # Display results
427
+ st.success("โœ… Batch Predictions Generated Successfully!")
428
+
429
+ # Summary metrics
430
+ col1, col2, col3, col4 = st.columns(4)
431
+
432
+ with col1:
433
+ st.metric("Total Records", len(predictions))
434
+
435
+ with col2:
436
+ successful = len([p for p in predictions if p is not None])
437
+ st.metric("Successful", successful)
438
+
439
+ with col3:
440
+ avg_prediction = sum([p for p in predictions if p is not None]) / successful
441
+ st.metric("Average Sales", f"โ‚น{avg_prediction:,.2f}")
442
+
443
+ with col4:
444
+ total_predicted = sum([p for p in predictions if p is not None])
445
+ st.metric("Total Predicted", f"โ‚น{total_predicted:,.2f}")
446
+
447
+ # Results table
448
+ st.subheader("๐Ÿ“Š Prediction Results")
449
+ st.dataframe(df_results)
450
+
451
+ # Download results
452
+ csv_results = df_results.to_csv(index=False)
453
+ st.download_button(
454
+ "๐Ÿ“ฅ Download Results",
455
+ csv_results,
456
+ "superkart_predictions.csv",
457
+ "text/csv"
458
+ )
459
+
460
+ # Visualization
461
+ if successful > 0:
462
+ st.subheader("๐Ÿ“ˆ Prediction Analysis")
463
+
464
+ # Distribution plot
465
+ valid_predictions = [p for p in predictions if p is not None]
466
+ fig = px.histogram(
467
+ x=valid_predictions,
468
+ title="Distribution of Predicted Sales",
469
+ labels={"x": "Predicted Sales (โ‚น)", "y": "Frequency"}
470
+ )
471
+ st.plotly_chart(fig, use_container_width=True)
472
+
473
+ else:
474
+ st.error(f"โŒ Batch Prediction Failed: {error}")
475
 
476
  except Exception as e:
477
+ st.error(f"โŒ Error reading file: {str(e)}")
478
+
479
+ else: # Manual Entry
480
+ st.subheader("โœ๏ธ Manual Entry")
481
+ st.markdown("Add multiple records manually for batch prediction.")
482
+
483
+ # Initialize session state for manual entries
484
+ if "manual_entries" not in st.session_state:
485
+ st.session_state.manual_entries = []
486
+
487
+ # Add new entry form
488
+ with st.expander("โž• Add New Entry"):
489
+ with st.form("manual_entry_form"):
490
+ col1, col2 = st.columns(2)
491
+
492
+ with col1:
493
+ weight = st.number_input("Weight (kg)", 0.1, 50.0, 12.5, key="manual_weight")
494
+ sugar = st.selectbox("Sugar Content", ["Low Sugar", "Regular", "No Sugar"], key="manual_sugar")
495
+ area = st.number_input("Display Area", 0.001, 1.0, 0.1, key="manual_area")
496
+ product_type = st.selectbox("Product Type", [
497
+ "Fruits and Vegetables", "Snack Foods", "Household", "Frozen Foods",
498
+ "Dairy", "Canned", "Baking Goods", "Health and Hygiene"
499
+ ], key="manual_type")
500
+ mrp = st.number_input("MRP (โ‚น)", 1.0, 500.0, 150.0, key="manual_mrp")
501
+
502
+ with col2:
503
+ size = st.selectbox("Store Size", ["Small", "Medium", "High"], key="manual_size")
504
+ city = st.selectbox("City Type", ["Tier 1", "Tier 2", "Tier 3"], key="manual_city")
505
+ store_type = st.selectbox("Store Type", [
506
+ "Departmental Store", "Supermarket Type1", "Supermarket Type2", "Food Mart"
507
+ ], key="manual_store_type")
508
+ age = st.number_input("Store Age", 0, 50, 15, key="manual_age")
509
+
510
+ if st.form_submit_button("โž• Add Entry"):
511
+ entry = {
512
+ "Product_Weight": weight,
513
+ "Product_Sugar_Content": sugar,
514
+ "Product_Allocated_Area": area,
515
+ "Product_Type": product_type,
516
+ "Product_MRP": mrp,
517
+ "Store_Size": size,
518
+ "Store_Location_City_Type": city,
519
+ "Store_Type": store_type,
520
+ "Store_Age": age
521
+ }
522
+ st.session_state.manual_entries.append(entry)
523
+ st.success("โœ… Entry added!")
524
+
525
+ # Display current entries
526
+ if st.session_state.manual_entries:
527
+ st.subheader(f"๐Ÿ“ Current Entries ({len(st.session_state.manual_entries)})")
528
+
529
+ # Convert to DataFrame for display
530
+ entries_df = pd.DataFrame(st.session_state.manual_entries)
531
+ st.dataframe(entries_df)
532
+
533
+ col1, col2 = st.columns(2)
534
+
535
+ with col1:
536
+ if st.button("๐Ÿš€ Generate Predictions"):
537
+ with st.spinner("๐Ÿ”ฎ Generating predictions..."):
538
+ result, error = make_batch_prediction(st.session_state.manual_entries)
539
+
540
+ if result:
541
+ predictions = result["predictions"]
542
+ entries_df["Predicted_Sales"] = predictions
543
+
544
+ st.success("โœ… Predictions Generated!")
545
+ st.dataframe(entries_df)
546
+
547
+ # Download option
548
+ csv_data = entries_df.to_csv(index=False)
549
+ st.download_button(
550
+ "๐Ÿ“ฅ Download Results",
551
+ csv_data,
552
+ "manual_predictions.csv",
553
+ "text/csv"
554
+ )
555
+ else:
556
+ st.error(f"โŒ Prediction Failed: {error}")
557
+
558
+ with col2:
559
+ if st.button("๐Ÿ—‘๏ธ Clear All Entries"):
560
+ st.session_state.manual_entries = []
561
+ st.experimental_rerun()
562
+
563
+ def analytics_dashboard_page():
564
+ """Analytics dashboard interface."""
565
+
566
+ st.header("๐Ÿ“ˆ Analytics Dashboard")
567
+ st.markdown("Explore sales patterns and model insights.")
568
+
569
+ # Mock analytics data for demonstration
570
+ st.subheader("๐ŸŽฏ Model Performance Metrics")
571
+
572
+ col1, col2, col3, col4 = st.columns(4)
573
+
574
+ with col1:
575
+ st.metric("Model Accuracy", "92.8%", "2.1%")
576
+
577
+ with col2:
578
+ st.metric("Avg Prediction Error", "โ‚น249", "-โ‚น15")
579
+
580
+ with col3:
581
+ st.metric("Total Predictions", "1,247", "156")
582
+
583
+ with col4:
584
+ st.metric("API Uptime", "99.2%", "0.3%")
585
+
586
+ # Feature importance chart
587
+ st.subheader("๐ŸŽฏ Feature Importance")
588
+
589
+ feature_data = {
590
+ "Feature": ["Product_Weight", "Product_MRP", "Store_Type", "City_Type", "Store_Size"],
591
+ "Importance": [0.35, 0.28, 0.18, 0.12, 0.07]
592
+ }
593
+
594
+ fig = px.bar(
595
+ x=feature_data["Importance"],
596
+ y=feature_data["Feature"],
597
+ orientation='h',
598
+ title="Top 5 Most Important Features",
599
+ labels={"x": "Importance Score", "y": "Features"}
600
+ )
601
+ fig.update_layout(yaxis={'categoryorder':'total ascending'})
602
+ st.plotly_chart(fig, use_container_width=True)
603
+
604
+ # Sample insights
605
+ st.subheader("๐Ÿ’ก Business Insights")
606
+
607
+ insight_tabs = st.tabs(["Store Performance", "Product Analysis", "Regional Trends"])
608
+
609
+ with insight_tabs[0]:
610
+ st.markdown("""
611
+ **Store Performance Insights:**
612
+ - Departmental Stores show 40% higher sales on average
613
+ - Medium-sized stores have the best cost-to-performance ratio
614
+ - Tier 1 cities generate 2.8x more revenue than Tier 3
615
+ """)
616
+
617
+ with insight_tabs[1]:
618
+ st.markdown("""
619
+ **Product Analysis:**
620
+ - Heavy products (>15kg) correlate with higher sales
621
+ - Premium MRP products (>โ‚น200) show better margins
622
+ - Dairy and Frozen Foods are top performing categories
623
+ """)
624
+
625
+ with insight_tabs[2]:
626
+ st.markdown("""
627
+ **Regional Trends:**
628
+ - Tier 1 cities: Focus on premium product mix
629
+ - Tier 2 cities: Balanced approach with growth potential
630
+ - Tier 3 cities: Price-sensitive, high-volume strategy
631
+ """)
632
+
633
+ def api_status_page():
634
+ """API status and configuration page."""
635
+
636
+ global API_BASE_URL
637
+
638
+ st.header("๐Ÿ”ง API Status & Configuration")
639
+
640
+ # API URL configuration
641
+ st.subheader("โš™๏ธ API Configuration")
642
+
643
+ current_url = st.text_input(
644
+ "Backend API URL",
645
+ value=API_BASE_URL,
646
+ help="Enter your backend API URL"
647
+ )
648
+
649
+ if st.button("๐Ÿ’พ Update API URL"):
650
+ API_BASE_URL = current_url
651
+ st.success("โœ… API URL updated!")
652
+
653
+ # Health check
654
+ st.subheader("๐Ÿฅ Health Check")
655
+
656
+ if st.button("๐Ÿ” Check API Health"):
657
+ with st.spinner("Checking API health..."):
658
+ health_status = check_api_health()
659
+
660
+ if health_status:
661
+ st.success("โœ… API is healthy and responsive!")
662
+
663
+ # Try to get API info
664
+ try:
665
+ response = requests.get(f"{API_BASE_URL}/", timeout=10)
666
+ if response.status_code == 200:
667
+ api_info = response.json()
668
+ st.json(api_info)
669
+ except:
670
+ pass
671
+ else:
672
+ st.error("โŒ API is not accessible")
673
+ st.markdown("""
674
+ **Troubleshooting:**
675
+ 1. Check if the API URL is correct
676
+ 2. Ensure the backend service is running
677
+ 3. Verify your internet connection
678
+ 4. Check if the API allows CORS requests
679
+ """)
680
+
681
+ # API documentation
682
+ st.subheader("๐Ÿ“š API Documentation")
683
+
684
+ st.markdown("""
685
+ **Available Endpoints:**
686
+
687
+ - `GET /` - API information and sample input
688
+ - `GET /health` - Health check endpoint
689
+ - `GET /model_info` - Model details and performance metrics
690
+ - `POST /predict` - Single prediction endpoint
691
+ - `POST /batch_predict` - Batch prediction endpoint
692
+
693
+ **Sample Request Format:**
694
+ ```json
695
+ {
696
+ "Product_Weight": 12.5,
697
+ "Product_Sugar_Content": "Low Sugar",
698
+ "Product_Allocated_Area": 0.15,
699
+ "Product_Type": "Fruits and Vegetables",
700
+ "Product_MRP": 150.0,
701
+ "Store_Size": "Medium",
702
+ "Store_Location_City_Type": "Tier 2",
703
+ "Store_Type": "Supermarket Type2",
704
+ "Store_Age": 15
705
+ }
706
+ ```
707
+ """)
708
 
709
+ # Footer
710
+ def show_footer():
711
+ """Show application footer."""
712
+ st.markdown("---")
713
+ st.markdown("""
714
+ <div style='text-align: center; color: #666;'>
715
+ <p>๐Ÿ›’ SuperKart Sales Forecasting System | Powered by AI & Machine Learning</p>
716
+ <p>Built with Streamlit & Flask | ยฉ 2025 SuperKart Analytics Team</p>
717
+ </div>
718
+ """, unsafe_allow_html=True)
719
 
720
+ # Run the application
721
+ if __name__ == "__main__":
722
+ main()
723
+ show_footer()