Spaces:
Runtime error
Runtime error
| # Import the os module for environment variable operations | |
| import os | |
| # Import streamlit for creating the web application interface | |
| import streamlit as st | |
| # Import pandas for data manipulation and DataFrame operations | |
| import pandas as pd | |
| # Import requests for making HTTP requests to the backend API | |
| import requests | |
| # Configure the Streamlit page with title, icon, and layout | |
| st.set_page_config(page_title="Super Kart Sales Predictor", page_icon="🛒", layout="centered") | |
| # API configuration (set your deployed Backend Space URL) | |
| # Get the backend URL from environment variable, with a default fallback | |
| default_backend = os.getenv("BACKEND_URL", "https://vksfdc2024-SuperKartbackend.hf.space") | |
| # Create a text input in the sidebar for users to modify the backend API URL | |
| api_base_url = st.sidebar.text_input( | |
| "Backend API base URL", # Label for the input field | |
| value=default_backend, # Default value from environment or fallback | |
| help="Example: https://<username>-backend.hf.space" # Help text for users | |
| ) | |
| # Helper function to normalize the base URL format | |
| def normalize_base(url: str) -> str: | |
| # Check if the URL is empty or None | |
| if not url: | |
| # Return empty string if no URL provided | |
| return "" | |
| # Remove leading and trailing whitespace from the URL | |
| url = url.strip() | |
| # Check if URL ends with a trailing slash | |
| if url.endswith('/'): | |
| # Remove the trailing slash for consistency | |
| url = url[:-1] | |
| # Return the normalized URL | |
| return url | |
| # Apply the normalization function to the API base URL | |
| api_base_url = normalize_base(api_base_url) | |
| # Display the main title of the application | |
| st.title("Super Kart - Product Store Sales Prediction") | |
| # Display descriptive text explaining what the app does | |
| st.write("Enter product and store details to predict Product_Store_Sales_Total.") | |
| # Create a sidebar section for additional controls | |
| with st.sidebar: | |
| # Create a button to test the backend health endpoint | |
| if st.button("Test backend /health"): | |
| # Start try block for error handling | |
| try: | |
| # Make a GET request to the health endpoint with 10-second timeout | |
| r = requests.get(f"{api_base_url}/health", timeout=10) | |
| # Display success message with status code and response text | |
| st.success(f"Health OK {r.status_code}: {r.text}") | |
| # Catch any exceptions during the health check | |
| except Exception as e: | |
| # Display error message with exception details | |
| st.error(f"Health check failed: {e}") | |
| # --------------------------------------------- | |
| # Input form | |
| # --------------------------------------------- | |
| # Create a form container for user inputs | |
| with st.form("predict_form"): | |
| # Create two columns for organizing input fields | |
| col1, col2 = st.columns(2) | |
| # Left column inputs | |
| with col1: | |
| # Text input for product identifier with default value | |
| Product_Identifier = st.text_input("Product Identifier", value="PROD_0001") | |
| # Number input for product weight with constraints and default | |
| Product_Weight = st.number_input("Product Weight", min_value=0.0, step=0.01, value=12.3) | |
| # Dropdown selection for product sugar content categories | |
| Product_Sugar_Content = st.selectbox( | |
| "Product Sugar Content", # Label for the selectbox | |
| ["Low Sugar", "Regular", "No Sugar"] # Available options | |
| ) | |
| # Number input for product allocated area with specific constraints | |
| Product_Allocated_Area = st.number_input("Product Allocated Area", min_value=0.0, max_value=1.0, step=0.0001, value=0.065) | |
| # Dropdown selection for product type with multiple categories | |
| Product_Type = st.selectbox( | |
| "Product Type", # Label for the selectbox | |
| [ # List of available product types | |
| "Fruits and Vegetables", "Snack Foods", "Household", "Frozen Foods", | |
| "Dairy", "Canned", "Baking Goods", "Health and Hygiene", | |
| "Soft Drinks", "Meat", "Breads", "Hard Drinks", | |
| "Starchy Foods", "Breakfast", "Seafood", "Others" | |
| ], | |
| index=1 # Default to second option (Snack Foods) | |
| ) | |
| # Right column inputs | |
| with col2: | |
| # Number input for Maximum Retail Price with constraints | |
| Product_MRP = st.number_input("Product MRP", min_value=0.0, step=0.01, value=249.99) | |
| # Text input for store identifier with default value | |
| Store_Identifier = st.text_input("Store Identifier", value="OUT013") | |
| # Number input for store establishment year with realistic range | |
| Store_Establishment_Year = st.number_input("Store Establishment Year", min_value=1900, max_value=2100, value=2009, step=1) | |
| # Dropdown selection for store size categories | |
| Store_Size = st.selectbox("Store Size", ["Small", "Medium", "High"], index=1) | |
| # Dropdown selection for store location tier | |
| Store_Location_Type = st.selectbox("Store Location Type", ["Tier 1", "Tier 2", "Tier 3"], index=1) | |
| # Dropdown selection for store type with multiple categories | |
| Store_Type = st.selectbox( | |
| "Store Type", # Label for the selectbox | |
| ["Supermarket Type1", "Supermarket Type2", "Supermarket Type3", "Grocery Store"], # Available options | |
| index=0 # Default to first option | |
| ) | |
| # Create a submit button for the form with primary styling | |
| submitted = st.form_submit_button("Predict", type="primary") | |
| # Prepare payload dictionary as expected by the backend API | |
| payload = { | |
| "Product_Identifier": Product_Identifier, # Product ID from form input | |
| "Product_Weight": Product_Weight, # Product weight from form input | |
| "Product_Sugar_Content": Product_Sugar_Content, # Sugar content selection from form | |
| "Product_Allocated_Area": Product_Allocated_Area, # Allocated area from form input | |
| "Product_Type": Product_Type, # Product type selection from form | |
| "Product_MRP": Product_MRP, # MRP from form input | |
| "Store_Identifier": Store_Identifier, # Store ID from form input | |
| "Store_Establishment_Year": int(Store_Establishment_Year), # Convert year to integer | |
| "Store_Size": Store_Size, # Store size selection from form | |
| "Store_Location_Type": Store_Location_Type, # Location type selection from form | |
| "Store_Type": Store_Type # Store type selection from form | |
| } | |
| # --------------------------------------------- | |
| # Single prediction | |
| # --------------------------------------------- | |
| # Check if the form was submitted | |
| if submitted: | |
| # Start try block for error handling | |
| try: | |
| # Construct the full URL for the single product prediction endpoint | |
| url = f"{api_base_url}/v1/product" | |
| # Make POST request to API with JSON payload and 30-second timeout | |
| resp = requests.post(url, json=payload, timeout=30) | |
| # Check if the response status code indicates success | |
| if resp.status_code == 200: | |
| # Parse the JSON response | |
| out = resp.json() | |
| # Display success message with the predicted sales value | |
| st.success(f"Predicted Sales: {out.get('Predicted_Sales', 'N/A')}") | |
| # Display additional information about the prediction | |
| st.caption(f"Product: {out.get('Product_Identifier', Product_Identifier)} | Store: {out.get('Store_Identifier', Store_Identifier)}") | |
| # Handle non-200 status codes | |
| else: | |
| # Try to parse error response as JSON | |
| try: | |
| # Display error message with JSON error details | |
| st.error(f"Request failed ({resp.status_code}): {resp.json()}") | |
| # If JSON parsing fails, show raw text | |
| except Exception: | |
| # Display error message with raw response text | |
| st.error(f"Request failed ({resp.status_code}): {resp.text}") | |
| # Catch any exceptions during API communication | |
| except Exception as e: | |
| # Display error message with exception details | |
| st.error(f"Error contacting API: {e}") | |
| # Add a horizontal line separator | |
| st.markdown("---") | |
| # --------------------------------------------- | |
| # Batch prediction | |
| # --------------------------------------------- | |
| # Display subheader for batch prediction section | |
| st.subheader("Batch Prediction (CSV Upload)") | |
| # Display instruction text for CSV upload | |
| st.write("Upload a CSV with the following columns:") | |
| # Define the list of required columns for batch prediction | |
| required_cols = [ | |
| "Product_Identifier", "Product_Weight", "Product_Sugar_Content", "Product_Allocated_Area", | |
| "Product_Type", "Product_MRP", "Store_Identifier", "Store_Establishment_Year", | |
| "Store_Size", "Store_Location_Type", "Store_Type" | |
| ] | |
| # Display the required columns in a code block format | |
| st.code(", ".join(required_cols)) | |
| # Provide a small downloadable template | |
| # Create a sample DataFrame with one row of example data | |
| template_df = pd.DataFrame([{ | |
| "Product_Identifier": "PROD_0001", # Example product ID | |
| "Product_Weight": 12.3, # Example weight | |
| "Product_Sugar_Content": "Regular", # Example sugar content | |
| "Product_Allocated_Area": 0.065, # Example allocated area | |
| "Product_Type": "Snack Foods", # Example product type | |
| "Product_MRP": 249.99, # Example MRP | |
| "Store_Identifier": "OUT013", # Example store ID | |
| "Store_Establishment_Year": 2009, # Example establishment year | |
| "Store_Size": "Medium", # Example store size | |
| "Store_Location_Type": "Tier 2", # Example location type | |
| "Store_Type": "Supermarket Type1" # Example store type | |
| }]) | |
| # Create a download button for the CSV template | |
| st.download_button( | |
| "Download CSV Template", # Button label | |
| data=template_df.to_csv(index=False), # Convert DataFrame to CSV without index | |
| file_name="super_kart_batch_template.csv", # Downloaded filename | |
| mime="text/csv" # MIME type for CSV files | |
| ) | |
| # Create file uploader widget for CSV files | |
| uploaded = st.file_uploader("Upload CSV", type=["csv"]) | |
| # Check if a file has been uploaded | |
| if uploaded is not None: | |
| # Start try block for error handling | |
| try: | |
| # Read the uploaded CSV file into a DataFrame | |
| df_preview = pd.read_csv(uploaded) | |
| # Display a preview of the first few rows | |
| st.write("Preview:", df_preview.head()) | |
| # Catch exceptions during CSV reading | |
| except Exception as e: | |
| # Display error message if CSV cannot be read | |
| st.error(f"Could not read CSV: {e}") | |
| # Create a button to trigger batch prediction | |
| if st.button("Predict for Batch", type="primary"): | |
| # Start try block for error handling | |
| try: | |
| # Construct the URL for batch prediction endpoint | |
| url = f"{api_base_url}/v1/productbatch" | |
| # Prepare file data for multipart form upload | |
| files = {"file": (uploaded.name, uploaded.getvalue(), "text/csv")} | |
| # Make POST request with file upload and 60-second timeout | |
| resp = requests.post(url, files=files, timeout=60) | |
| # Check if the response status code indicates success | |
| if resp.status_code == 200: | |
| # Parse the JSON response containing predictions | |
| result = resp.json() | |
| # Display success message | |
| st.success("Batch predictions received.") | |
| # Convert the result dictionary to a DataFrame for display | |
| st.write(pd.DataFrame(list(result.items()), columns=["Product_Identifier", "Predicted_Sales"])) | |
| # Handle non-200 status codes | |
| else: | |
| # Try to parse error response as JSON | |
| try: | |
| # Display error message with JSON error details | |
| st.error(f"Request failed ({resp.status_code}): {resp.json()}") | |
| # If JSON parsing fails, show raw text | |
| except Exception: | |
| # Display error message with raw response text | |
| st.error(f"Request failed ({resp.status_code}): {resp.text}") | |
| # Catch any exceptions during batch API communication | |
| except Exception as e: | |
| # Display error message with exception details | |
| st.error(f"Error contacting API: {e}") | |