AjaykumarPilla commited on
Commit
632f432
Β·
verified Β·
1 Parent(s): 2d585ca

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +121 -87
app.py CHANGED
@@ -1,133 +1,167 @@
1
  import streamlit as st
2
  import pandas as pd
 
3
  from prophet import Prophet
4
  from datetime import datetime, timedelta
 
5
  import plotly.graph_objects as go
6
 
 
 
 
 
 
 
 
 
 
 
7
  # Prepare data for Prophet
8
  def prepare_prophet_data(usage_series):
 
9
  end_date = datetime.now()
10
  start_date = end_date - timedelta(days=len(usage_series) - 1)
11
  dates = [start_date + timedelta(days=i) for i in range(len(usage_series))]
12
 
13
- return pd.DataFrame({'ds': dates, 'y': usage_series})
14
-
15
- # Train model with fixed low sensitivity
16
- def train_model(usage_series):
17
- df = prepare_prophet_data(usage_series)
18
- model = Prophet(
19
- yearly_seasonality=False,
20
- weekly_seasonality=True,
21
- daily_seasonality=True,
22
- changepoint_prior_scale=0.05, # Low sensitivity to recent changes
23
- changepoint_range=0.6
24
- )
25
- model.fit(df)
26
  return model
27
 
28
- # Forecast future usage
29
  def make_forecast(model, periods):
30
  future = model.make_future_dataframe(periods=periods)
31
  forecast = model.predict(future)
32
- return forecast, round(forecast['yhat'].tail(periods).sum())
33
 
34
- # Validate input
35
  def validate_usage_series(usage_str):
36
  try:
37
- usage = [float(x) for x in usage_str.split(',')]
38
- if len(usage) != 60:
39
- return None, "Please enter exactly 60 values."
40
- if any(x < 0 for x in usage):
41
  return None, "Usage values must be non-negative."
42
- return usage, None
43
  except:
44
- return None, "Invalid format. Use 60 comma-separated numbers."
45
 
46
- # Main app
47
  def main():
48
  st.title("SmartLab Consumables Forecast")
49
-
 
 
 
 
50
  st.header("Input Parameters")
51
  consumable_type = st.selectbox("Consumable Type", ['Filters', 'Reagents', 'Vials'])
52
- usage_input = st.text_input("Last 60 Days Usage (comma-separated)", "")
53
  current_stock = st.number_input("Current Stock", min_value=0, value=0)
54
-
55
  if st.button("Generate Forecast"):
56
- usage_series, error = validate_usage_series(usage_input)
 
57
  if error:
58
  st.error(error)
59
  return
60
-
61
- model = train_model(usage_series)
62
- forecast_df, forecast_7 = make_forecast(model, 7)
63
- _, forecast_14 = make_forecast(model, 14)
64
- _, forecast_30 = make_forecast(model, 30)
65
-
 
 
 
 
 
 
 
 
66
  st.header("Forecast Results")
67
- st.write(f"πŸ“Š **7-Day Forecast**: {forecast_7} units")
68
- st.write(f"πŸ“Š **14-Day Forecast**: {forecast_14} units")
69
- st.write(f"πŸ“Š **30-Day Forecast**: {forecast_30} units")
70
-
71
- st.header("Threshold Alerts 🚨")
72
- alert_7 = current_stock < forecast_7
73
- alert_14 = current_stock < forecast_14
74
- alert_30 = current_stock < forecast_30
75
-
76
- if alert_7:
77
- st.warning(f"🚩 Current stock ({current_stock}) is below 7-day forecast ({forecast_7})")
78
- if alert_14:
79
- st.warning(f"🚩 Current stock ({current_stock}) is below 14-day forecast ({forecast_14})")
80
- if alert_30:
81
- st.warning(f"🚩 Current stock ({current_stock}) is below 30-day forecast ({forecast_30})")
82
-
83
- st.header("Order Suggestions πŸ›’")
84
- st.write(f"➑️ **7 Days**: Order {max(0, forecast_7 - current_stock)} units")
85
- st.write(f"➑️ **14 Days**: Order {max(0, forecast_14 - current_stock)} units")
86
- st.write(f"➑️ **30 Days**: Order {max(0, forecast_30 - current_stock)} units")
87
-
88
- # Forecast trend line
89
- st.header("Forecast Trend")
90
- fig_curve = go.Figure()
91
- fig_curve.add_trace(go.Scatter(
92
- x=forecast_df['ds'],
93
- y=forecast_df['yhat'],
94
- mode='lines',
95
- name='Predicted Usage',
96
- line=dict(color='blue')
 
 
 
 
 
 
 
97
  ))
98
- fig_curve.update_layout(
99
- title="Forecast Curve",
100
- xaxis_title="Date",
101
- yaxis_title="Units",
102
- template="plotly_white"
103
  )
104
- st.plotly_chart(fig_curve)
105
-
106
- # Threshold bar chart
107
- st.header("Stock vs Forecast 🚦")
108
  alert_data = pd.DataFrame({
109
  'Category': ['Current Stock', '7-Day Forecast', '14-Day Forecast', '30-Day Forecast'],
110
  'Units': [current_stock, forecast_7, forecast_14, forecast_30],
111
- 'Flag': [False, alert_7, alert_14, alert_30]
 
 
 
 
 
112
  })
113
- colors = ['green'] + ['red' if a else 'blue' for a in alert_data['Flag'][1:]]
114
- flags = ["" if not f else "🚩" for f in alert_data['Flag']]
115
-
116
- fig_alert = go.Figure()
117
- fig_alert.add_trace(go.Bar(
118
  x=alert_data['Category'],
119
  y=alert_data['Units'],
120
- marker_color=colors,
121
- text=flags,
122
  textposition='auto'
123
  ))
124
- fig_alert.update_layout(
125
- title="Threshold Alerts Overview",
126
- xaxis_title="Category",
127
- yaxis_title="Units",
128
- template="plotly_white"
129
  )
130
- st.plotly_chart(fig_alert)
131
 
132
  if __name__ == "__main__":
133
- main()
 
1
  import streamlit as st
2
  import pandas as pd
3
+ import pickle
4
  from prophet import Prophet
5
  from datetime import datetime, timedelta
6
+ import numpy as np
7
  import plotly.graph_objects as go
8
 
9
+ # Load the trained models (optional, for initialization or fallback)
10
+ @st.cache_resource
11
+ def load_model():
12
+ try:
13
+ with open('model.pkl', 'rb') as f:
14
+ models = pickle.load(f)
15
+ return models
16
+ except FileNotFoundError:
17
+ return None
18
+
19
  # Prepare data for Prophet
20
  def prepare_prophet_data(usage_series):
21
+ # Create a date range for the last 60 days
22
  end_date = datetime.now()
23
  start_date = end_date - timedelta(days=len(usage_series) - 1)
24
  dates = [start_date + timedelta(days=i) for i in range(len(usage_series))]
25
 
26
+ # Create Prophet-compatible DataFrame
27
+ prophet_df = pd.DataFrame({
28
+ 'ds': dates,
29
+ 'y': usage_series
30
+ })
31
+ return prophet_df
32
+
33
+ # Train or update Prophet model with user-provided usage series
34
+ def train_model_with_usage(usage_series):
35
+ prophet_df = prepare_prophet_data(usage_series)
36
+ model = Prophet(yearly_seasonality=False, weekly_seasonality=True, daily_seasonality=True)
37
+ model.fit(prophet_df)
 
38
  return model
39
 
40
+ # Function to make forecasts
41
  def make_forecast(model, periods):
42
  future = model.make_future_dataframe(periods=periods)
43
  forecast = model.predict(future)
44
+ return round(forecast['yhat'].tail(periods).sum()) # Round to nearest integer
45
 
46
+ # Function to validate input
47
  def validate_usage_series(usage_str):
48
  try:
49
+ usage_list = [float(x) for x in usage_str.split(',')]
50
+ if len(usage_list) != 60:
51
+ return None, "Usage series must contain exactly 60 values."
52
+ if any(x < 0 for x in usage_list):
53
  return None, "Usage values must be non-negative."
54
+ return usage_list, None
55
  except:
56
+ return None, "Invalid usage series format. Please enter 60 comma-separated numbers."
57
 
58
+ # Main Streamlit app
59
  def main():
60
  st.title("SmartLab Consumables Forecast")
61
+
62
+ # Load pre-trained models (optional, for reference)
63
+ models = load_model()
64
+
65
+ # Input form
66
  st.header("Input Parameters")
67
  consumable_type = st.selectbox("Consumable Type", ['Filters', 'Reagents', 'Vials'])
68
+ usage_series = st.text_input("Last 60 Days Usage (comma-separated)", "")
69
  current_stock = st.number_input("Current Stock", min_value=0, value=0)
70
+
71
  if st.button("Generate Forecast"):
72
+ # Validate inputs
73
+ usage_list, error = validate_usage_series(usage_series)
74
  if error:
75
  st.error(error)
76
  return
77
+
78
+ # Train a new model with the user-provided usage series
79
+ try:
80
+ model = train_model_with_usage(usage_list)
81
+ except Exception as e:
82
+ st.error(f"Error training model: {str(e)}")
83
+ return
84
+
85
+ # Forecast for 7, 14, and 30 days
86
+ forecast_7 = make_forecast(model, 7)
87
+ forecast_14 = make_forecast(model, 14)
88
+ forecast_30 = make_forecast(model, 30)
89
+
90
+ # Display forecasts
91
  st.header("Forecast Results")
92
+ st.write(f"**7-Day Forecast**: {forecast_7} units")
93
+ st.write(f"**14-Day Forecast**: {forecast_14} units")
94
+ st.write(f"**30-Day Forecast**: {forecast_30} units")
95
+
96
+ # Threshold alerting
97
+ st.header("Threshold Alerts")
98
+ if current_stock < forecast_7:
99
+ st.warning(f"Alert: Current stock ({current_stock}) is below 7-day forecast ({forecast_7}). 🚩")
100
+ if current_stock < forecast_14:
101
+ st.warning(f"Alert: Current stock ({current_stock}) is below 14-day forecast ({forecast_14}). 🚩")
102
+ if current_stock < forecast_30:
103
+ st.warning(f"Alert: Current stock ({current_stock}) is below 30-day forecast ({forecast_30}). 🚩")
104
+
105
+ # Order suggestions
106
+ st.header("Order Suggestions")
107
+ order_7 = max(0, round(forecast_7 - current_stock)) # Round to nearest integer
108
+ order_14 = max(0, round(forecast_14 - current_stock)) # Round to nearest integer
109
+ order_30 = max(0, round(forecast_30 - current_stock)) # Round to nearest integer
110
+
111
+ st.write(f"**For 7 Days**: Order {order_7} additional units.")
112
+ st.write(f"**For 14 Days**: Order {order_14} additional units.")
113
+ st.write(f"**For 30 Days**: Order {order_30} additional units.")
114
+
115
+ # Graphical representation for forecast
116
+ st.header("Forecast Visualization")
117
+ forecast_data = pd.DataFrame({
118
+ 'Period': ['7 Days', '14 Days', '30 Days'],
119
+ 'Units': [forecast_7, forecast_14, forecast_30]
120
+ })
121
+ fig_forecast = go.Figure()
122
+ fig_forecast.add_trace(go.Scatter(
123
+ x=forecast_data['Period'],
124
+ y=forecast_data['Units'],
125
+ mode='lines+markers',
126
+ name='Forecasted Units',
127
+ line=dict(color='blue'),
128
+ marker=dict(size=10)
129
  ))
130
+ fig_forecast.update_layout(
131
+ title='Consumable Usage Forecast',
132
+ xaxis_title='Time Period',
133
+ yaxis_title='Units',
134
+ template='plotly_white'
135
  )
136
+ st.plotly_chart(fig_forecast)
137
+
138
+ # Graphical representation for threshold alerts
139
+ st.header("Threshold Alerts Visualization")
140
  alert_data = pd.DataFrame({
141
  'Category': ['Current Stock', '7-Day Forecast', '14-Day Forecast', '30-Day Forecast'],
142
  'Units': [current_stock, forecast_7, forecast_14, forecast_30],
143
+ 'Alert': [
144
+ False,
145
+ current_stock < forecast_7,
146
+ current_stock < forecast_14,
147
+ current_stock < forecast_30
148
+ ]
149
  })
150
+ fig_alerts = go.Figure()
151
+ fig_alerts.add_trace(go.Bar(
 
 
 
152
  x=alert_data['Category'],
153
  y=alert_data['Units'],
154
+ marker_color=['green'] + ['red' if alert else 'blue' for alert in alert_data['Alert'][1:]],
155
+ text=[f"🚩" if alert else "" for alert in alert_data['Alert']],
156
  textposition='auto'
157
  ))
158
+ fig_alerts.update_layout(
159
+ title='Stock vs Forecast with Alerts (🚩 indicates low stock)',
160
+ xaxis_title='Category',
161
+ yaxis_title='Units',
162
+ template='plotly_white'
163
  )
164
+ st.plotly_chart(fig_alerts)
165
 
166
  if __name__ == "__main__":
167
+ main()