vinodcwanted commited on
Commit
215519e
·
verified ·
1 Parent(s): 962911d

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. Dockerfile +9 -9
  2. app.py +93 -85
  3. requirements.txt +8 -0
Dockerfile CHANGED
@@ -1,16 +1,16 @@
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 8501 and make it accessible externally
14
- CMD ["streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0", "--server.enableXsrfProtection=false"]
15
-
16
- # NOTE: Disable XSRF protection for easier external access in order to make batch predictions
 
 
 
1
  FROM python:3.9-slim
2
 
3
+ # Set the working directory inside the container
4
  WORKDIR /app
5
 
6
+ # Copy all files from the current directory to the container's working directory
7
  COPY . .
8
 
9
+ # Install dependencies from the requirements file without using cache to reduce image size
10
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
11
 
12
+ # Define the command to start the application using Gunicorn with 4 worker processes
13
+ # - `-w 4`: Uses 4 worker processes for handling requests
14
+ # - `-b 0.0.0.0:7860`: Binds the server to port 7860 on all network interfaces
15
+ # - `app:app`: Runs the Flask app (assuming `app.py` contains the Flask instance named `app`)
16
+ CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:7860", "app:app"]
app.py CHANGED
@@ -1,88 +1,96 @@
1
- import streamlit as st
2
- import requests
 
 
 
3
  import pandas as pd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
- st.set_page_config(page_title="SuperKart Sales Prediction", page_icon="🛒", layout="centered")
6
- st.title("🛒 SuperKart Sales Prediction")
7
-
8
- # --- API base (set your HF Space URL here) ---
9
- api_url = st.text_input(
10
- "API Base URL",
11
- value="https://vinodcwanted-SuperKart.hf.space",
12
- help="Your Flask API base (no trailing slash). Example: https://thiresh-rentalpricepredictionbackend.hf.space",
13
- )
14
-
15
- st.markdown("### Product Details")
16
- col1, col2 = st.columns(2)
17
- with col1:
18
- product_weight = st.number_input("Product_Weight", min_value=0.0, value=12.5, step=0.1)
19
- product_alloc_area = st.number_input("Product_Allocated_Area", min_value=0.0, value=0.05, step=0.001, format="%.3f")
20
- product_mrp = st.number_input("Product_MRP", min_value=0.0, value=150.0, step=0.1)
21
- product_id = st.text_input("Product_Id (optional)", value="", help="If provided, API derives product_categories from its prefix (FD/NC/DR).")
22
- with col2:
23
- product_sugar_content = st.selectbox("Product_Sugar_Content", ["Low Sugar", "Regular", "No Sugar"])
24
- product_type = st.selectbox(
25
- "Product_Type",
26
- [
27
- "Frozen Foods","Dairy","Canned","Baking Goods","Health and Hygiene","Snack Foods","Meat",
28
- "Household","Hard Drinks","Fruits and Vegetables","Breads","Soft Drinks","Breakfast",
29
- "Others","Starchy Foods","Seafood"
30
- ],
31
- )
32
- product_categories = st.selectbox(
33
- "product_categories (optional)",
34
- ["(leave blank)","FD","NC","DR"],
35
- help="If left blank, API will derive from Product_Id (if provided)."
36
- )
37
-
38
- st.markdown("### Store Details")
39
- col3, col4 = st.columns(2)
40
- with col3:
41
- store_size = st.selectbox("Store_Size", ["Small", "Medium", "High"])
42
- store_type = st.selectbox("Store_Type", ["Departmental Store", "Supermarket Type1", "Supermarket Type2", "Food Mart"])
43
- with col4:
44
- store_city_type = st.selectbox("Store_Location_City_Type", ["Tier 1", "Tier 2", "Tier 3"])
45
- store_est_year = st.number_input("Store_Establishment_Year", min_value=1900, max_value=2025, value=2005, step=1,
46
- help="API will compute Establishment_age = 2025 - this year.")
47
-
48
- # Build payload (single JSON)
49
- payload = {
50
- "Product_Weight": product_weight,
51
- "Product_Sugar_Content": product_sugar_content,
52
- "Product_Allocated_Area": product_alloc_area,
53
- "Product_Type": product_type,
54
- "Product_MRP": product_mrp,
55
- "Store_Establishment_Year": int(store_est_year),
56
- "Store_Size": store_size,
57
- "Store_Location_City_Type": store_city_type,
58
- "Store_Type": store_type,
59
- }
60
-
61
- # Optional fields
62
- if product_id.strip():
63
- payload["Product_Id"] = product_id.strip()
64
- if product_categories != "(leave blank)":
65
- payload["product_categories"] = product_categories
66
-
67
- st.markdown("---")
68
- if st.button("Predict"):
69
- if not api_url.strip():
70
- st.error("Please enter your API Base URL.")
71
  else:
72
- try:
73
- endpoint = api_url.rstrip("/") + "/v1/sale"
74
- with st.spinner("Contacting API..."):
75
- resp = requests.post(endpoint, json=payload, timeout=30)
76
- if resp.status_code == 200:
77
- data = resp.json()
78
- if "prediction" in data:
79
- st.success(f"Predicted Sales: **{data['prediction']}**")
80
- else:
81
- st.warning(f"API responded without 'prediction' key:\n{data}")
82
- else:
83
- st.error(f"API error {resp.status_code}: {resp.text}")
84
- except Exception as e:
85
- st.error(f"Request failed: {e}")
86
-
87
- with st.expander("Show request payload"):
88
- st.json(payload)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Flask API for SuperKart Product_Store_Sales_Total prediction (single JSON only)
2
+
3
+ import os
4
+ import joblib
5
+ import numpy as np
6
  import pandas as pd
7
+ from flask import Flask, request, jsonify
8
+
9
+ APP_NAME = "SuperKart Sales Predictor"
10
+ MODEL_FILENAME = "SuperKart_prediction_model_v1_0.joblib"
11
+
12
+ CURRENT_YEAR = 2025
13
+ VALID_PRODUCT_PREFIXES = {"FD", "NC", "DR"} # allowed values for product_categories
14
+
15
+ app = Flask(APP_NAME)
16
+
17
+ # ---- Load model (ensure the .joblib is in the same run directory) ----
18
+ model = joblib.load(MODEL_FILENAME)
19
+
20
+ def derive_features(df: pd.DataFrame) -> pd.DataFrame:
21
+ """
22
+ - Standardize 'Product_Sugar_Content' ('reg' -> 'Regular')
23
+ - Create 'Establishment_age' if 'Store_Establishment_Year' exists
24
+ - Ensure 'product_categories' exists and is one of ('FD','NC','DR'):
25
+ * If provided, clean & validate to those values
26
+ * Else, derive from first two chars of 'Product_Id'
27
+ """
28
+ df = df.copy()
29
+
30
+ # Fix sugar typo
31
+ if "Product_Sugar_Content" in df.columns:
32
+ df["Product_Sugar_Content"] = df["Product_Sugar_Content"].replace({"reg": "Regular"})
33
+
34
+ # Establishment_age
35
+ if "Establishment_age" not in df.columns and "Store_Establishment_Year" in df.columns:
36
+ df["Establishment_age"] = CURRENT_YEAR - pd.to_numeric(
37
+ df["Store_Establishment_Year"], errors="coerce"
38
+ )
39
 
40
+ # product_categories
41
+ if "product_categories" in df.columns:
42
+ df["product_categories"] = df["product_categories"].astype(str).str.upper().str.strip()
43
+ df.loc[~df["product_categories"].isin(VALID_PRODUCT_PREFIXES), "product_categories"] = pd.NA
44
+ # If you prefer "Other" instead of NA for invalid values, use:
45
+ # df.loc[~df["product_categories"].isin(VALID_PRODUCT_PREFIXES), "product_categories"] = "Other"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  else:
47
+ if "Product_Id" in df.columns:
48
+ prefix = df["Product_Id"].astype(str).str[:2].str.upper()
49
+ df["product_categories"] = np.where(prefix.isin(VALID_PRODUCT_PREFIXES), prefix, pd.NA)
50
+ else:
51
+ df["product_categories"] = pd.NA
52
+
53
+ return df
54
+
55
+ def to_float(val):
56
+ try:
57
+ f = float(val)
58
+ return 0.0 if f == 0.0 else round(f, 2)
59
+ except Exception:
60
+ return None
61
+
62
+ @app.get("/")
63
+ def home():
64
+ return jsonify({
65
+ "message": f"Welcome to the {APP_NAME} API!",
66
+ "model_file": MODEL_FILENAME,
67
+ "product_categories_allowed_values": sorted(list(VALID_PRODUCT_PREFIXES))
68
+ })
69
+
70
+ @app.post("/v1/sale")
71
+ def predict_single():
72
+ """
73
+ Accepts a single JSON object and returns one prediction.
74
+ Example keys:
75
+ Product_Weight, Product_Sugar_Content, Product_Allocated_Area, Product_Type,
76
+ Product_MRP, Store_Establishment_Year (optional if Establishment_age passed),
77
+ Store_Size, Store_Location_City_Type, Store_Type, Product_Id (optional if product_categories passed)
78
+ """
79
+ try:
80
+ payload = request.get_json(force=True, silent=False)
81
+ if not isinstance(payload, dict):
82
+ return jsonify({"error": "Payload must be a single JSON object."}), 400
83
+
84
+ df = pd.DataFrame([payload])
85
+ df = derive_features(df)
86
+
87
+ pred = model.predict(df)[0]
88
+ return jsonify({"prediction": to_float(pred)})
89
+
90
+ except ValueError as ve:
91
+ return jsonify({"error": str(ve)}), 400
92
+ except Exception as e:
93
+ return jsonify({"error": f"Unexpected error: {str(e)}"}), 500
94
+
95
+ if __name__ == "__main__":
96
+ app.run(debug=True)
requirements.txt CHANGED
@@ -1,3 +1,11 @@
1
  pandas==2.2.2
 
 
 
 
 
 
 
2
  requests==2.32.3
 
3
  streamlit==1.43.2
 
1
  pandas==2.2.2
2
+ numpy==2.0.2
3
+ scikit-learn==1.6.1
4
+ xgboost==2.1.4
5
+ joblib==1.4.2
6
+ Werkzeug==2.2.2
7
+ flask==2.2.2
8
+ gunicorn==20.1.0
9
  requests==2.32.3
10
+ uvicorn[standard]
11
  streamlit==1.43.2