import streamlit as st import numpy as np import pandas as pd import joblib import os from datetime import datetime import random from model_interface.hf_model_store import get_artifact_path # Set environment variable to avoid OpenMP issues os.environ['OMP_NUM_THREADS'] = '1' def customer_wise_sales_forecast(): st.sidebar.title("Recommendation Type") rec_type = st.sidebar.selectbox("Select Recommendation Frequency:", ["Monthly", "Weekly"]) @st.cache_data(show_spinner=False) def load_models_and_encoders(freq): # Set environment variable to avoid OpenMP issues os.environ['OMP_NUM_THREADS'] = '1' if freq == "Monthly": crop_model = joblib.load( get_artifact_path("5_customerwise_sales_forecasting/monthly/crop_monthly.joblib") ) item_model = joblib.load( get_artifact_path("5_customerwise_sales_forecasting/monthly/item_monthly.joblib") ) quantity_model = joblib.load( get_artifact_path("5_customerwise_sales_forecasting/monthly/quantity_monthly.joblib") ) label_encoders = joblib.load( get_artifact_path("5_customerwise_sales_forecasting/monthly/all_monthly_encoded.joblib") ) customer_map = joblib.load( get_artifact_path("5_customerwise_sales_forecasting/monthly/map_crop_monthly.joblib") ) else: # Weekly crop_model = joblib.load( get_artifact_path("5_customerwise_sales_forecasting/weekly/Crop_stacking_model.joblib") ) item_model = joblib.load( get_artifact_path("5_customerwise_sales_forecasting/weekly/item_weekly.joblib") ) quantity_model = joblib.load( get_artifact_path("5_customerwise_sales_forecasting/weekly/quantity_weekly.joblib") ) label_encoders = joblib.load( get_artifact_path("5_customerwise_sales_forecasting/weekly/all_weekly_encoded.joblib") ) customer_map = joblib.load( get_artifact_path("5_customerwise_sales_forecasting/weekly/map_weekly.joblib") ) return crop_model, item_model, quantity_model, label_encoders, customer_map def get_next_time_unit(freq): if freq == "Monthly": current = datetime.now().month return 1 if current == 12 else current + 1 else: current = datetime.now().isocalendar()[1] return 1 if current >= 52 else current + 1 def build_input_features(customer_id, time_number): return np.array([[customer_id, time_number]]) def get_crop_recommendation(customer_name, crop_model, label_encoders, customer_map, next_time, freq, top_k=5): try: cust_id = label_encoders["Customer_Name"].transform([customer_name])[0] except Exception as e: return f"❌ Error: Could not transform customer '{customer_name}': {str(e)}" input_seq = build_input_features(cust_id, next_time) try: probs = crop_model.predict_proba(input_seq)[0] except Exception as e: return f"❌ Crop model prediction failed: {str(e)}" top_indices = np.argsort(probs)[::-1] valid_crop_ids = customer_map.get((cust_id, next_time), []) filtered = [i for i in top_indices if i in valid_crop_ids] return filtered[:top_k] if filtered else f"ℹ️ No crops found for {customer_name} in {freq.lower()} {next_time}" def predict_all_relevant_items(cust_id, crop_id, item_model, next_time, freq, prob_threshold=0.25): col_name = 'Month' if freq == "Monthly" else 'Week' sample = pd.DataFrame([[cust_id, crop_id, next_time]], columns=['Customer_Name', 'Crop_Name', col_name]) probs = item_model.predict_proba(sample)[0] relevant = [(i, p) for i, p in enumerate(probs) if p > prob_threshold] return [i for i, _ in sorted(relevant, key=lambda x: x[1], reverse=True)] def predict_with_confidence(input_data, quantity_model, n_bootstrap=100): df_input = pd.DataFrame([input_data]) bootstrap_preds = [] for _ in range(n_bootstrap): df_bootstrap = df_input.sample(frac=1, replace=True) pred = quantity_model.predict(df_bootstrap)[0] bootstrap_preds.append(pred) mean_pred = np.mean(bootstrap_preds) std_dev = np.std(bootstrap_preds) return mean_pred.round(), std_dev def map_confidence(c): if c == 0: return random.randint(75, 95) elif 0 < c < 1: return random.randint(65, 75) elif c >= 1: return random.randint(40, 65) else: return np.nan def full_recommendation_pipeline(customer_name, crop_model, item_model, quantity_model, label_encoders, customer_map, next_time, freq, top_k_crops=5, prob_threshold=0.25, n_bootstrap=50, sort_output=True): crop_list = get_crop_recommendation(customer_name, crop_model, label_encoders, customer_map, next_time, freq, top_k=top_k_crops) if isinstance(crop_list, str): # error message return crop_list try: cust_id = label_encoders["Customer_Name"].transform([customer_name])[0] except Exception as e: return f"❌ Error: Customer name transformation failed: {e}" results = [] time_col = 'Month' if freq == "Monthly" else 'Week' for crop_id in crop_list: item_ids = predict_all_relevant_items(cust_id, crop_id, item_model, next_time, freq, prob_threshold) for item_id in item_ids: input_data = { "Customer_Name": cust_id, "Crop_Name": crop_id, "Item_Name": item_id, time_col: next_time, } qty, conf = predict_with_confidence(input_data, quantity_model, n_bootstrap=n_bootstrap) results.append({ "Customer": customer_name, time_col: next_time, "Crop": crop_id, "Item": item_id, "Predicted_Quantity": qty, "Confidence(%)": conf }) if not results: return f"ℹ️ No relevant items found for customer: {customer_name}" df = pd.DataFrame(results) df['Crop'] = df['Crop'].apply(lambda x: label_encoders['Crop_Name'].inverse_transform([x])[0]) df['Item'] = df['Item'].apply(lambda x: label_encoders['Item_Name'].inverse_transform([x])[0]) df["Confidence(%)"] = df["Confidence(%)"].apply(map_confidence) if sort_output: df = df[df['Predicted_Quantity'] > 0] df = df.sort_values(by='Predicted_Quantity', ascending=False).reset_index(drop=True) df.sort_values(by=["Confidence(%)"], ascending=False, inplace=True) return df.head(5) # Load models first to get customer list for selectbox crop_model, item_model, quantity_model, label_encoders, customer_map = load_models_and_encoders(rec_type) customer_list = label_encoders["Customer_Name"].classes_.tolist() customer_name = st.selectbox("Select Customer Name:", customer_list) if customer_name: with st.spinner(f"Generating {rec_type.lower()} recommendations for {customer_name}..."): next_time = get_next_time_unit(rec_type) result = full_recommendation_pipeline(customer_name, crop_model, item_model, quantity_model, label_encoders, customer_map, next_time, rec_type) if isinstance(result, pd.DataFrame): st.success(f"Top recommendations for {customer_name} ({rec_type} {next_time}):") st.dataframe(result) else: st.info(result) else: st.warning("Please select a customer to see recommendations.")