AjaykumarPilla commited on
Commit
8861c59
·
verified ·
1 Parent(s): bb11f58

Upload 3 files

Browse files
Files changed (3) hide show
  1. app.py +176 -0
  2. model.pkl +3 -0
  3. requirements.txt +7 -0
app.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import numpy as np
4
+ import torch
5
+ import torch.nn as nn
6
+ import pickle
7
+ import plotly.express as px
8
+ from sklearn.preprocessing import MinMaxScaler
9
+
10
+ # Define the LSTM model class (same as in train_model.py)
11
+ class LSTMModel(nn.Module):
12
+ def __init__(self, input_size=1, hidden_size=50, num_layers=2):
13
+ super(LSTMModel, self).__init__()
14
+ self.hidden_size = hidden_size
15
+ self.num_layers = num_layers
16
+ self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True)
17
+ self.fc = nn.Linear(hidden_size, 1)
18
+
19
+ def forward(self, x):
20
+ h0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size)
21
+ c0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size)
22
+ out, _ = self.lstm(x, (h0, c0))
23
+ out = self.fc(out[:, -1, :])
24
+ return out
25
+
26
+ # Load the pre-trained model and scaler
27
+ @st.cache_resource
28
+ def load_model_and_scaler():
29
+ with open("model.pkl", "rb") as f:
30
+ saved_data = pickle.load(f)
31
+ model = saved_data['model']
32
+ scaler = saved_data['scaler']
33
+ model.eval()
34
+ return model, scaler
35
+
36
+ # Load and preprocess data
37
+ @st.cache_data
38
+ def load_and_preprocess_data():
39
+ df = pd.read_excel("SmartLab_Consumables_Forecast_Sample.xlsx")
40
+ df['Usage_Date'] = pd.to_datetime(df['Usage_Date'])
41
+ return df
42
+
43
+ # Function to generate forecasts using the model
44
+ def generate_forecasts(df, model, scaler, sequence_length=10):
45
+ consumables = df['Consumable_Name'].unique()
46
+ forecasts = []
47
+
48
+ for consumable in consumables:
49
+ consumable_data = df[df['Consumable_Name'] == consumable].set_index('Usage_Date')
50
+ consumable_data = consumable_data['Quantity_Used'].resample('D').sum().fillna(0)
51
+ if len(consumable_data) < sequence_length:
52
+ continue
53
+
54
+ # Scale the data
55
+ scaled_data = scaler.transform(consumable_data.values.reshape(-1, 1))
56
+
57
+ # Prepare the last sequence for prediction
58
+ last_sequence = scaled_data[-sequence_length:]
59
+ last_sequence = torch.FloatTensor(last_sequence).unsqueeze(0) # Shape: (1, sequence_length, 1)
60
+
61
+ # Predict future usage
62
+ with torch.no_grad():
63
+ pred_7 = model(last_sequence).numpy()
64
+ pred_14 = model(last_sequence).numpy() * 2 # Simplified scaling for 14 days
65
+ pred_30 = model(last_sequence).numpy() * 4 # Simplified scaling for 30 days
66
+
67
+ # Inverse transform predictions
68
+ pred_7 = scaler.inverse_transform(pred_7)[0][0]
69
+ pred_14 = scaler.inverse_transform(pred_14)[0][0]
70
+ pred_30 = scaler.inverse_transform(pred_30)[0][0]
71
+
72
+ # Add to forecasts
73
+ latest_date = consumable_data.index.max()
74
+ for _, row in df[df['Consumable_Name'] == consumable].iterrows():
75
+ forecasts.append({
76
+ 'Lab_ID': row['Lab_ID'],
77
+ 'Consumable_Name': consumable,
78
+ 'Usage_Date': latest_date,
79
+ 'Quantity_Used': row['Quantity_Used'],
80
+ 'Current_Stock': row['Current_Stock'],
81
+ 'Reorder_Threshold': row['Reorder_Threshold'],
82
+ 'Forecast_7_Days': pred_7,
83
+ 'Forecast_14_Days': pred_14,
84
+ 'Forecast_30_Days': pred_30,
85
+ 'Reorder_Recommended': row['Current_Stock'] < row['Reorder_Threshold'] or (row['Current_Stock'] - pred_30) < row['Reorder_Threshold'],
86
+ 'Order_Suggestion': max(pred_30 - row['Current_Stock'] + row['Reorder_Threshold'], 0) if (row['Current_Stock'] < row['Reorder_Threshold'] or (row['Current_Stock'] - pred_30) < row['Reorder_Threshold']) else 0,
87
+ 'Forecast_Generated_Date': pd.to_datetime('2025-06-06')
88
+ })
89
+
90
+ return pd.DataFrame(forecasts)
91
+
92
+ # Streamlit app
93
+ def main():
94
+ st.set_page_config(page_title="SmartLab Consumables Forecasting", layout="wide")
95
+ st.title("SmartLab Consumables Forecasting Dashboard")
96
+ st.markdown("Forecast consumable usage, monitor stock thresholds, and get order suggestions.")
97
+
98
+ # Load model and data
99
+ with st.spinner("Loading model and data..."):
100
+ model, scaler = load_model_and_scaler()
101
+ df = load_and_preprocess_data()
102
+ forecast_df = generate_forecasts(df, model, scaler)
103
+
104
+ # Sidebar filters
105
+ st.sidebar.header("Filters")
106
+ labs = ['All Labs'] + sorted(forecast_df['Lab_ID'].unique().tolist())
107
+ selected_lab = st.sidebar.selectbox("Filter by Lab", labs)
108
+
109
+ consumables = ['All Consumables'] + sorted(forecast_df['Consumable_Name'].unique().tolist())
110
+ selected_consumable = st.sidebar.selectbox("Filter by Consumable", consumables)
111
+
112
+ # Apply filters
113
+ filtered_df = forecast_df.copy()
114
+ if selected_lab != 'All Labs':
115
+ filtered_df = filtered_df[filtered_df['Lab_ID'] == selected_lab]
116
+ if selected_consumable != 'All Consumables':
117
+ filtered_df = filtered_df[filtered_df['Consumable_Name'] == selected_consumable]
118
+
119
+ # Forecast Overview Chart
120
+ st.header("Forecast Overview")
121
+ chart_data = filtered_df.groupby(['Lab_ID', 'Consumable_Name']).agg({
122
+ 'Forecast_7_Days': 'mean',
123
+ 'Forecast_14_Days': 'mean',
124
+ 'Forecast_30_Days': 'mean'
125
+ }).reset_index()
126
+ chart_data['name'] = chart_data['Lab_ID'] + ' - ' + chart_data['Consumable_Name']
127
+ chart_data = chart_data.melt(id_vars=['name'], value_vars=['Forecast_7_Days', 'Forecast_14_Days', 'Forecast_30_Days'],
128
+ var_name='Forecast_Period', value_name='Forecasted_Usage')
129
+
130
+ fig = px.line(chart_data, x='name', y='Forecasted_Usage', color='Forecast_Period',
131
+ labels={'Forecasted_Usage': 'Forecasted Usage', 'name': 'Lab - Consumable'},
132
+ color_discrete_map={
133
+ 'Forecast_7_Days': '#8884d8',
134
+ 'Forecast_14_Days': '#82ca9d',
135
+ 'Forecast_30_Days': '#ff7300'
136
+ })
137
+ fig.update_layout(
138
+ xaxis_title="Lab - Consumable",
139
+ yaxis_title="Forecasted Usage",
140
+ xaxis_tickangle=45,
141
+ margin=dict(b=150),
142
+ legend_title_text='Forecast Period'
143
+ )
144
+ st.plotly_chart(fig, use_container_width=True)
145
+
146
+ # Inventory Status Table
147
+ st.header("Inventory Status")
148
+ display_df = filtered_df.copy()
149
+ display_df['Reorder_Recommended'] = display_df['Reorder_Recommended'].apply(lambda x: 'Yes 🚩' if x else 'No')
150
+ display_df['Order_Suggestion'] = display_df['Order_Suggestion'].apply(lambda x: f"{x:.0f} units" if x > 0 else "None")
151
+ display_df = display_df[[
152
+ 'Lab_ID', 'Consumable_Name', 'Current_Stock', 'Reorder_Threshold',
153
+ 'Forecast_7_Days', 'Forecast_14_Days', 'Forecast_30_Days',
154
+ 'Reorder_Recommended', 'Order_Suggestion'
155
+ ]]
156
+ # Apply conditional styling
157
+ def highlight_reorder(row):
158
+ return ['background-color: #fee2e2' if row['Reorder_Recommended'] == 'Yes 🚩' else '' for _ in row]
159
+ styled_df = display_df.style.apply(highlight_reorder, axis=1).format({
160
+ 'Forecast_7_Days': '{:.2f}',
161
+ 'Forecast_14_Days': '{:.2f}',
162
+ 'Forecast_30_Days': '{:.2f}'
163
+ })
164
+ st.dataframe(styled_df, use_container_width=True)
165
+
166
+ # Interesting Fact
167
+ st.header("Interesting Fact")
168
+ max_forecast = chart_data[chart_data['Forecast_Period'] == 'Forecast_30_Days'].nlargest(1, 'Forecasted_Usage')
169
+ if not max_forecast.empty:
170
+ st.info(
171
+ f"Did you know? The consumable with the highest 30-day forecast is **{max_forecast['name'].iloc[0]}** "
172
+ f"at **{max_forecast['Forecasted_Usage'].iloc[0]:.2f} units**, indicating a significant upcoming demand!"
173
+ )
174
+
175
+ if __name__ == "__main__":
176
+ main()
model.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:83cb56b3d8af822dfd20570b08fe98294b0bd0a58604b3846f68b93f4d2537d8
3
+ size 129057
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ pandas
2
+ numpy
3
+ torch
4
+ scikit-learn
5
+ openpyxl
6
+ streamlit
7
+ plotly