Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import requests | |
| import json | |
| import pandas as pd | |
| import plotly.express as px | |
| import plotly.graph_objects as go | |
| from datetime import datetime | |
| import time | |
| # Page configuration | |
| st.set_page_config( | |
| page_title="SuperKart Sales Forecasting", | |
| page_icon="🛒", | |
| layout="wide", | |
| initial_sidebar_state="expanded" | |
| ) | |
| # Custom CSS for better styling | |
| st.markdown(""" | |
| <style> | |
| .main-header { | |
| font-size: 3rem; | |
| color: #1f77b4; | |
| text-align: center; | |
| margin-bottom: 2rem; | |
| } | |
| .metric-card { | |
| background-color: #f0f2f6; | |
| padding: 1rem; | |
| border-radius: 0.5rem; | |
| border-left: 4px solid #1f77b4; | |
| } | |
| .prediction-result { | |
| font-size: 2rem; | |
| font-weight: bold; | |
| color: #2e8b57; | |
| text-align: center; | |
| padding: 1rem; | |
| background-color: #f0fff0; | |
| border-radius: 0.5rem; | |
| border: 2px solid #2e8b57; | |
| } | |
| .error-message { | |
| color: #dc3545; | |
| background-color: #f8d7da; | |
| padding: 1rem; | |
| border-radius: 0.5rem; | |
| border: 1px solid #f5c6cb; | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| # API Configuration | |
| API_BASE_URL = "https://your-backend-api-url.hf.space" # Replace with your actual API URL | |
| def check_api_health(): | |
| """Check if the API is healthy and accessible.""" | |
| try: | |
| response = requests.get(f"{API_BASE_URL}/health", timeout=10) | |
| return response.status_code == 200 | |
| except: | |
| return False | |
| def make_prediction(data): | |
| """Make a single prediction using the API.""" | |
| try: | |
| response = requests.post( | |
| f"{API_BASE_URL}/predict", | |
| json=data, | |
| headers={"Content-Type": "application/json"}, | |
| timeout=30 | |
| ) | |
| if response.status_code == 200: | |
| return response.json(), None | |
| else: | |
| return None, f"API Error: {response.status_code} - {response.text}" | |
| except requests.exceptions.Timeout: | |
| return None, "Request timeout. Please try again." | |
| except requests.exceptions.ConnectionError: | |
| return None, "Cannot connect to API. Please check your internet connection." | |
| except Exception as e: | |
| return None, f"Unexpected error: {str(e)}" | |
| def make_batch_prediction(data_list): | |
| """Make batch predictions using the API.""" | |
| try: | |
| response = requests.post( | |
| f"{API_BASE_URL}/batch_predict", | |
| json=data_list, | |
| headers={"Content-Type": "application/json"}, | |
| timeout=60 | |
| ) | |
| if response.status_code == 200: | |
| return response.json(), None | |
| else: | |
| return None, f"API Error: {response.status_code} - {response.text}" | |
| except requests.exceptions.Timeout: | |
| return None, "Request timeout. Please try again." | |
| except requests.exceptions.ConnectionError: | |
| return None, "Cannot connect to API. Please check your internet connection." | |
| except Exception as e: | |
| return None, f"Unexpected error: {str(e)}" | |
| def main(): | |
| """Main Streamlit application.""" | |
| # Main header | |
| st.markdown('<h1 class="main-header">🛒 SuperKart Sales Forecasting</h1>', unsafe_allow_html=True) | |
| st.markdown("### AI-Powered Sales Prediction for Retail Excellence") | |
| # Sidebar for navigation | |
| st.sidebar.title("🎯 Navigation") | |
| app_mode = st.sidebar.selectbox("Choose the app mode", | |
| ["Single Prediction", "Batch Prediction", "Analytics Dashboard", "API Status"]) | |
| # API Health Check | |
| with st.sidebar: | |
| st.markdown("---") | |
| st.subheader("🔧 API Status") | |
| if st.button("Check API Health"): | |
| with st.spinner("Checking API..."): | |
| if check_api_health(): | |
| st.success("✅ API is healthy") | |
| else: | |
| st.error("❌ API is not accessible") | |
| st.markdown("---") | |
| st.markdown(""" | |
| **Features:** | |
| - 🎯 Single Prediction | |
| - 📊 Batch Predictions | |
| - 📈 Analytics Dashboard | |
| - 🔍 Real-time Validation | |
| """) | |
| # Main content based on selected mode | |
| if app_mode == "Single Prediction": | |
| single_prediction_page() | |
| elif app_mode == "Batch Prediction": | |
| batch_prediction_page() | |
| elif app_mode == "Analytics Dashboard": | |
| analytics_dashboard_page() | |
| elif app_mode == "API Status": | |
| api_status_page() | |
| def single_prediction_page(): | |
| """Single prediction interface.""" | |
| st.header("🎯 Single Sales Prediction") | |
| st.markdown("Enter product and store details to get an instant sales forecast.") | |
| # Create input form | |
| with st.form("prediction_form"): | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| st.subheader("📦 Product Details") | |
| product_weight = st.number_input( | |
| "Product Weight (kg)", | |
| min_value=0.1, | |
| max_value=50.0, | |
| value=12.5, | |
| step=0.1, | |
| help="Weight of the product in kilograms" | |
| ) | |
| product_sugar_content = st.selectbox( | |
| "Sugar Content", | |
| ["Low Sugar", "Regular", "No Sugar"], | |
| help="Sugar content category of the product" | |
| ) | |
| product_allocated_area = st.number_input( | |
| "Allocated Display Area", | |
| min_value=0.001, | |
| max_value=1.0, | |
| value=0.1, | |
| step=0.001, | |
| format="%.3f", | |
| help="Ratio of allocated display area (0-1)" | |
| ) | |
| product_type = st.selectbox( | |
| "Product Type", | |
| [ | |
| "Fruits and Vegetables", "Snack Foods", "Household", "Frozen Foods", | |
| "Dairy", "Canned", "Baking Goods", "Health and Hygiene", "Meat", | |
| "Soft Drinks", "Hard Drinks", "Starchy Foods", "Breakfast", | |
| "Seafood", "Bread", "Others" | |
| ], | |
| help="Category of the product" | |
| ) | |
| product_mrp = st.number_input( | |
| "Maximum Retail Price (₹)", | |
| min_value=1.0, | |
| max_value=500.0, | |
| value=150.0, | |
| step=1.0, | |
| help="Maximum retail price in rupees" | |
| ) | |
| with col2: | |
| st.subheader("🏪 Store Details") | |
| store_size = st.selectbox( | |
| "Store Size", | |
| ["Small", "Medium", "High"], | |
| index=1, | |
| help="Size category of the store" | |
| ) | |
| store_location_city_type = st.selectbox( | |
| "City Type", | |
| ["Tier 1", "Tier 2", "Tier 3"], | |
| index=1, | |
| help="Tier classification of the city" | |
| ) | |
| store_type = st.selectbox( | |
| "Store Type", | |
| ["Departmental Store", "Supermarket Type1", "Supermarket Type2", "Food Mart"], | |
| index=2, | |
| help="Type/format of the store" | |
| ) | |
| store_age = st.number_input( | |
| "Store Age (years)", | |
| min_value=0, | |
| max_value=50, | |
| value=15, | |
| step=1, | |
| help="Age of the store in years" | |
| ) | |
| with col3: | |
| st.subheader("📊 Prediction Summary") | |
| st.markdown(""" | |
| **Input Validation:** | |
| - All fields are required | |
| - Weights: 0.1 - 50 kg | |
| - Display Area: 0.001 - 1.0 | |
| - MRP: ₹1 - ₹500 | |
| - Store Age: 0 - 50 years | |
| """) | |
| st.markdown("---") | |
| st.markdown("**Business Context:**") | |
| st.markdown("This prediction helps with:") | |
| st.markdown("- Inventory planning") | |
| st.markdown("- Revenue forecasting") | |
| st.markdown("- Store optimization") | |
| st.markdown("- Regional strategy") | |
| # Submit button | |
| submitted = st.form_submit_button("🚀 Predict Sales", use_container_width=True) | |
| if submitted: | |
| # Prepare data for API | |
| prediction_data = { | |
| "Product_Weight": product_weight, | |
| "Product_Sugar_Content": product_sugar_content, | |
| "Product_Allocated_Area": product_allocated_area, | |
| "Product_Type": product_type, | |
| "Product_MRP": product_mrp, | |
| "Store_Size": store_size, | |
| "Store_Location_City_Type": store_location_city_type, | |
| "Store_Type": store_type, | |
| "Store_Age": store_age | |
| } | |
| # Make prediction | |
| with st.spinner("🔮 Generating prediction..."): | |
| result, error = make_prediction(prediction_data) | |
| if result: | |
| prediction = result["prediction"] | |
| # Display result | |
| st.success("✅ Prediction Generated Successfully!") | |
| # Main prediction result | |
| st.markdown( | |
| f'<div class="prediction-result">💰 Predicted Sales: ₹{prediction:,.2f}</div>', | |
| unsafe_allow_html=True | |
| ) | |
| # Additional insights | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| st.metric( | |
| "Daily Revenue", | |
| f"₹{prediction:,.2f}", | |
| delta=f"{prediction*0.1:,.2f}", | |
| delta_color="normal" | |
| ) | |
| with col2: | |
| monthly_estimate = prediction * 30 | |
| st.metric( | |
| "Monthly Estimate", | |
| f"₹{monthly_estimate:,.2f}", | |
| delta="Projected", | |
| delta_color="off" | |
| ) | |
| with col3: | |
| annual_estimate = prediction * 365 | |
| st.metric( | |
| "Annual Potential", | |
| f"₹{annual_estimate:,.2f}", | |
| delta="Estimated", | |
| delta_color="off" | |
| ) | |
| # Business recommendations | |
| st.markdown("### 💡 Business Insights") | |
| if prediction > 4000: | |
| st.success("🎯 **High Performance Expected** - This product-store combination shows excellent potential!") | |
| elif prediction > 2500: | |
| st.info("📈 **Good Performance Expected** - Solid sales potential with room for optimization.") | |
| else: | |
| st.warning("⚠️ **Moderate Performance Expected** - Consider promotional strategies or product mix optimization.") | |
| # Performance category analysis | |
| if store_location_city_type == "Tier 1" and store_type == "Departmental Store": | |
| st.info("🏆 **Premium Market Position** - Tier 1 Departmental Store typically shows highest performance.") | |
| if product_weight > 15: | |
| st.info("📦 **Heavy Product Advantage** - Higher weight products tend to generate more sales.") | |
| if product_mrp > 200: | |
| st.info("💎 **Premium Product** - High MRP products often indicate better margins.") | |
| else: | |
| st.error(f"❌ Prediction Failed: {error}") | |
| st.markdown(""" | |
| **Troubleshooting Steps:** | |
| 1. Check your internet connection | |
| 2. Verify API URL in the sidebar | |
| 3. Ensure all input values are within valid ranges | |
| 4. Try again in a few moments | |
| """) | |
| def batch_prediction_page(): | |
| """Batch prediction interface.""" | |
| st.header("📊 Batch Sales Prediction") | |
| st.markdown("Upload a CSV file or enter multiple records for bulk predictions.") | |
| # Option selection | |
| batch_option = st.radio( | |
| "Choose input method:", | |
| ["Upload CSV File", "Manual Entry"] | |
| ) | |
| if batch_option == "Upload CSV File": | |
| st.subheader("📁 Upload CSV File") | |
| # File upload | |
| uploaded_file = st.file_uploader( | |
| "Choose a CSV file", | |
| type="csv", | |
| help="Upload a CSV file with the required columns" | |
| ) | |
| # Show required format | |
| with st.expander("📋 Required CSV Format"): | |
| sample_df = pd.DataFrame({ | |
| "Product_Weight": [12.5, 16.2, 8.9], | |
| "Product_Sugar_Content": ["Low Sugar", "Regular", "No Sugar"], | |
| "Product_Allocated_Area": [0.1, 0.15, 0.05], | |
| "Product_Type": ["Fruits and Vegetables", "Dairy", "Snack Foods"], | |
| "Product_MRP": [150.0, 180.0, 95.0], | |
| "Store_Size": ["Medium", "High", "Small"], | |
| "Store_Location_City_Type": ["Tier 2", "Tier 1", "Tier 3"], | |
| "Store_Type": ["Supermarket Type2", "Departmental Store", "Food Mart"], | |
| "Store_Age": [15, 20, 8] | |
| }) | |
| st.dataframe(sample_df) | |
| if uploaded_file is not None: | |
| try: | |
| # Read CSV | |
| df = pd.read_csv(uploaded_file) | |
| st.success(f"✅ File uploaded successfully! {len(df)} records found.") | |
| # Show preview | |
| st.subheader("📋 Data Preview") | |
| st.dataframe(df.head()) | |
| # Validate columns | |
| required_columns = [ | |
| "Product_Weight", "Product_Sugar_Content", "Product_Allocated_Area", | |
| "Product_Type", "Product_MRP", "Store_Size", | |
| "Store_Location_City_Type", "Store_Type", "Store_Age" | |
| ] | |
| missing_columns = [col for col in required_columns if col not in df.columns] | |
| if missing_columns: | |
| st.error(f"❌ Missing required columns: {missing_columns}") | |
| else: | |
| st.success("✅ All required columns found!") | |
| if st.button("🚀 Generate Batch Predictions"): | |
| # Convert DataFrame to list of dictionaries | |
| data_list = df.to_dict('records') | |
| with st.spinner(f"🔮 Generating predictions for {len(data_list)} records..."): | |
| result, error = make_batch_prediction(data_list) | |
| if result: | |
| predictions = result["predictions"] | |
| # Add predictions to DataFrame | |
| df_results = df.copy() | |
| df_results["Predicted_Sales"] = predictions | |
| # Display results | |
| st.success("✅ Batch Predictions Generated Successfully!") | |
| # Summary metrics | |
| col1, col2, col3, col4 = st.columns(4) | |
| with col1: | |
| st.metric("Total Records", len(predictions)) | |
| with col2: | |
| successful = len([p for p in predictions if p is not None]) | |
| st.metric("Successful", successful) | |
| with col3: | |
| avg_prediction = sum([p for p in predictions if p is not None]) / successful | |
| st.metric("Average Sales", f"₹{avg_prediction:,.2f}") | |
| with col4: | |
| total_predicted = sum([p for p in predictions if p is not None]) | |
| st.metric("Total Predicted", f"₹{total_predicted:,.2f}") | |
| # Results table | |
| st.subheader("📊 Prediction Results") | |
| st.dataframe(df_results) | |
| # Download results | |
| csv_results = df_results.to_csv(index=False) | |
| st.download_button( | |
| "📥 Download Results", | |
| csv_results, | |
| "superkart_predictions.csv", | |
| "text/csv" | |
| ) | |
| # Visualization | |
| if successful > 0: | |
| st.subheader("📈 Prediction Analysis") | |
| # Distribution plot | |
| valid_predictions = [p for p in predictions if p is not None] | |
| fig = px.histogram( | |
| x=valid_predictions, | |
| title="Distribution of Predicted Sales", | |
| labels={"x": "Predicted Sales (₹)", "y": "Frequency"} | |
| ) | |
| st.plotly_chart(fig, use_container_width=True) | |
| else: | |
| st.error(f"❌ Batch Prediction Failed: {error}") | |
| except Exception as e: | |
| st.error(f"❌ Error reading file: {str(e)}") | |
| else: # Manual Entry | |
| st.subheader("✏️ Manual Entry") | |
| st.markdown("Add multiple records manually for batch prediction.") | |
| # Initialize session state for manual entries | |
| if "manual_entries" not in st.session_state: | |
| st.session_state.manual_entries = [] | |
| # Add new entry form | |
| with st.expander("➕ Add New Entry"): | |
| with st.form("manual_entry_form"): | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| weight = st.number_input("Weight (kg)", 0.1, 50.0, 12.5, key="manual_weight") | |
| sugar = st.selectbox("Sugar Content", ["Low Sugar", "Regular", "No Sugar"], key="manual_sugar") | |
| area = st.number_input("Display Area", 0.001, 1.0, 0.1, key="manual_area") | |
| product_type = st.selectbox("Product Type", [ | |
| "Fruits and Vegetables", "Snack Foods", "Household", "Frozen Foods", | |
| "Dairy", "Canned", "Baking Goods", "Health and Hygiene" | |
| ], key="manual_type") | |
| mrp = st.number_input("MRP (₹)", 1.0, 500.0, 150.0, key="manual_mrp") | |
| with col2: | |
| size = st.selectbox("Store Size", ["Small", "Medium", "High"], key="manual_size") | |
| city = st.selectbox("City Type", ["Tier 1", "Tier 2", "Tier 3"], key="manual_city") | |
| store_type = st.selectbox("Store Type", [ | |
| "Departmental Store", "Supermarket Type1", "Supermarket Type2", "Food Mart" | |
| ], key="manual_store_type") | |
| age = st.number_input("Store Age", 0, 50, 15, key="manual_age") | |
| if st.form_submit_button("➕ Add Entry"): | |
| entry = { | |
| "Product_Weight": weight, | |
| "Product_Sugar_Content": sugar, | |
| "Product_Allocated_Area": area, | |
| "Product_Type": product_type, | |
| "Product_MRP": mrp, | |
| "Store_Size": size, | |
| "Store_Location_City_Type": city, | |
| "Store_Type": store_type, | |
| "Store_Age": age | |
| } | |
| st.session_state.manual_entries.append(entry) | |
| st.success("✅ Entry added!") | |
| # Display current entries | |
| if st.session_state.manual_entries: | |
| st.subheader(f"📝 Current Entries ({len(st.session_state.manual_entries)})") | |
| # Convert to DataFrame for display | |
| entries_df = pd.DataFrame(st.session_state.manual_entries) | |
| st.dataframe(entries_df) | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| if st.button("🚀 Generate Predictions"): | |
| with st.spinner("🔮 Generating predictions..."): | |
| result, error = make_batch_prediction(st.session_state.manual_entries) | |
| if result: | |
| predictions = result["predictions"] | |
| entries_df["Predicted_Sales"] = predictions | |
| st.success("✅ Predictions Generated!") | |
| st.dataframe(entries_df) | |
| # Download option | |
| csv_data = entries_df.to_csv(index=False) | |
| st.download_button( | |
| "📥 Download Results", | |
| csv_data, | |
| "manual_predictions.csv", | |
| "text/csv" | |
| ) | |
| else: | |
| st.error(f"❌ Prediction Failed: {error}") | |
| with col2: | |
| if st.button("🗑️ Clear All Entries"): | |
| st.session_state.manual_entries = [] | |
| st.experimental_rerun() | |
| def analytics_dashboard_page(): | |
| """Analytics dashboard interface.""" | |
| st.header("📈 Analytics Dashboard") | |
| st.markdown("Explore sales patterns and model insights.") | |
| # Mock analytics data for demonstration | |
| st.subheader("🎯 Model Performance Metrics") | |
| col1, col2, col3, col4 = st.columns(4) | |
| with col1: | |
| st.metric("Model Accuracy", "92.8%", "2.1%") | |
| with col2: | |
| st.metric("Avg Prediction Error", "₹249", "-₹15") | |
| with col3: | |
| st.metric("Total Predictions", "1,247", "156") | |
| with col4: | |
| st.metric("API Uptime", "99.2%", "0.3%") | |
| # Feature importance chart | |
| st.subheader("🎯 Feature Importance") | |
| feature_data = { | |
| "Feature": ["Product_Weight", "Product_MRP", "Store_Type", "City_Type", "Store_Size"], | |
| "Importance": [0.35, 0.28, 0.18, 0.12, 0.07] | |
| } | |
| fig = px.bar( | |
| x=feature_data["Importance"], | |
| y=feature_data["Feature"], | |
| orientation='h', | |
| title="Top 5 Most Important Features", | |
| labels={"x": "Importance Score", "y": "Features"} | |
| ) | |
| fig.update_layout(yaxis={'categoryorder':'total ascending'}) | |
| st.plotly_chart(fig, use_container_width=True) | |
| # Sample insights | |
| st.subheader("💡 Business Insights") | |
| insight_tabs = st.tabs(["Store Performance", "Product Analysis", "Regional Trends"]) | |
| with insight_tabs[0]: | |
| st.markdown(""" | |
| **Store Performance Insights:** | |
| - Departmental Stores show 40% higher sales on average | |
| - Medium-sized stores have the best cost-to-performance ratio | |
| - Tier 1 cities generate 2.8x more revenue than Tier 3 | |
| """) | |
| with insight_tabs[1]: | |
| st.markdown(""" | |
| **Product Analysis:** | |
| - Heavy products (>15kg) correlate with higher sales | |
| - Premium MRP products (>₹200) show better margins | |
| - Dairy and Frozen Foods are top performing categories | |
| """) | |
| with insight_tabs[2]: | |
| st.markdown(""" | |
| **Regional Trends:** | |
| - Tier 1 cities: Focus on premium product mix | |
| - Tier 2 cities: Balanced approach with growth potential | |
| - Tier 3 cities: Price-sensitive, high-volume strategy | |
| """) | |
| def api_status_page(): | |
| """API status and configuration page.""" | |
| global API_BASE_URL | |
| st.header("🔧 API Status & Configuration") | |
| # API URL configuration | |
| st.subheader("⚙️ API Configuration") | |
| current_url = st.text_input( | |
| "Backend API URL", | |
| value=API_BASE_URL, | |
| help="Enter your backend API URL" | |
| ) | |
| if st.button("💾 Update API URL"): | |
| API_BASE_URL = current_url | |
| st.success("✅ API URL updated!") | |
| # Health check | |
| st.subheader("🏥 Health Check") | |
| if st.button("🔍 Check API Health"): | |
| with st.spinner("Checking API health..."): | |
| health_status = check_api_health() | |
| if health_status: | |
| st.success("✅ API is healthy and responsive!") | |
| # Try to get API info | |
| try: | |
| response = requests.get(f"{API_BASE_URL}/", timeout=10) | |
| if response.status_code == 200: | |
| api_info = response.json() | |
| st.json(api_info) | |
| except: | |
| pass | |
| else: | |
| st.error("❌ API is not accessible") | |
| st.markdown(""" | |
| **Troubleshooting:** | |
| 1. Check if the API URL is correct | |
| 2. Ensure the backend service is running | |
| 3. Verify your internet connection | |
| 4. Check if the API allows CORS requests | |
| """) | |
| # API documentation | |
| st.subheader("📚 API Documentation") | |
| st.markdown(""" | |
| **Available Endpoints:** | |
| - `GET /` - API information and sample input | |
| - `GET /health` - Health check endpoint | |
| - `GET /model_info` - Model details and performance metrics | |
| - `POST /predict` - Single prediction endpoint | |
| - `POST /batch_predict` - Batch prediction endpoint | |
| **Sample Request Format:** | |
| ```json | |
| { | |
| "Product_Weight": 12.5, | |
| "Product_Sugar_Content": "Low Sugar", | |
| "Product_Allocated_Area": 0.15, | |
| "Product_Type": "Fruits and Vegetables", | |
| "Product_MRP": 150.0, | |
| "Store_Size": "Medium", | |
| "Store_Location_City_Type": "Tier 2", | |
| "Store_Type": "Supermarket Type2", | |
| "Store_Age": 15 | |
| } | |
| ``` | |
| """) | |
| # Footer | |
| def show_footer(): | |
| """Show application footer.""" | |
| st.markdown("---") | |
| st.markdown(""" | |
| <div style='text-align: center; color: #666;'> | |
| <p>🛒 SuperKart Sales Forecasting System | Powered by AI & Machine Learning</p> | |
| <p>Built with Streamlit & Flask | © 2025 SuperKart Analytics Team</p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # Run the application | |
| if __name__ == "__main__": | |
| main() | |
| show_footer() | |