entropy25 commited on
Commit
dc6daaa
Β·
verified Β·
1 Parent(s): 4a7ad8a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +150 -229
app.py CHANGED
@@ -3,280 +3,201 @@ import pandas as pd
3
  import numpy as np
4
  import plotly.express as px
5
  import plotly.graph_objects as go
6
- from datetime import datetime
7
  import google.generativeai as genai
8
- import json
9
 
10
  # Page config
11
- st.set_page_config(
12
- page_title="Production Data Analysis",
13
- page_icon="🏭",
14
- layout="wide"
15
- )
16
 
17
- # Initialize Gemini
18
  @st.cache_resource
19
- def init_gemini():
20
  api_key = st.secrets.get("GOOGLE_API_KEY", "")
21
  if api_key:
22
  genai.configure(api_key=api_key)
23
  return genai.GenerativeModel('gemini-1.5-flash')
24
  return None
25
 
26
- # Data processing functions
27
  @st.cache_data
28
- def process_data(df):
29
- """Process and analyze production data"""
30
  df['date'] = pd.to_datetime(df['date'], format='%m/%d/%Y')
31
- df['day_of_week'] = df['date'].dt.day_name()
32
- df['week'] = df['date'].dt.isocalendar().week
33
- df['month'] = df['date'].dt.month
34
- df['is_weekend'] = df['day_of_week'].isin(['Saturday', 'Sunday'])
35
  return df
36
 
37
- def generate_summary(df):
38
- """Generate summary statistics"""
39
- total_production = df['weight_kg'].sum()
40
- total_items = len(df)
41
- daily_avg = df.groupby('date')['weight_kg'].sum().mean()
42
-
43
- summary = {
44
- 'total_production': total_production,
45
- 'total_items': total_items,
46
- 'daily_avg': daily_avg,
47
- 'date_range': f"{df['date'].min().strftime('%Y-%m-%d')} to {df['date'].max().strftime('%Y-%m-%d')}",
48
- 'production_days': df['date'].nunique()
49
- }
50
-
51
- # Material breakdown
52
- material_stats = {}
53
  for material in df['material_type'].unique():
54
- mat_data = df[df['material_type'] == material]
55
- material_stats[material] = {
56
- 'total': mat_data['weight_kg'].sum(),
57
- 'percentage': mat_data['weight_kg'].sum() / total_production * 100,
58
- 'count': len(mat_data)
 
 
 
59
  }
60
-
61
- summary['materials'] = material_stats
62
- return summary
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
64
- def detect_anomalies(df):
65
- """Detect production anomalies"""
66
- anomalies = {}
67
  for material in df['material_type'].unique():
68
- mat_data = df[df['material_type'] == material]
69
- Q1 = mat_data['weight_kg'].quantile(0.25)
70
- Q3 = mat_data['weight_kg'].quantile(0.75)
71
  IQR = Q3 - Q1
72
- lower_bound = Q1 - 1.5 * IQR
73
- upper_bound = Q3 + 1.5 * IQR
74
 
75
- outliers = mat_data[(mat_data['weight_kg'] < lower_bound) |
76
- (mat_data['weight_kg'] > upper_bound)]
77
-
78
- anomalies[material] = {
79
- 'count': len(outliers),
80
- 'normal_range': f"{lower_bound:.1f} - {upper_bound:.1f} kg",
81
- 'dates': outliers['date'].dt.strftime('%Y-%m-%d').tolist()[:5]
82
  }
83
-
84
- return anomalies
85
 
86
- def create_plots(df):
87
- """Create all visualization plots"""
88
- plots = {}
89
-
90
- # Daily production trend
91
- daily_total = df.groupby('date')['weight_kg'].sum().reset_index()
92
- plots['overview'] = px.line(
93
- daily_total, x='date', y='weight_kg',
94
- title='Daily Production Trend',
95
- labels={'weight_kg': 'Total Weight (kg)', 'date': 'Date'}
96
- )
97
-
98
- # Material comparison
99
- daily_by_material = df.groupby(['date', 'material_type'])['weight_kg'].sum().reset_index()
100
- plots['materials'] = px.line(
101
- daily_by_material, x='date', y='weight_kg', color='material_type',
102
- title='Production by Material Type'
103
- )
104
-
105
- # Weekly pattern
106
- weekly_pattern = df.groupby(['day_of_week', 'material_type'])['weight_kg'].mean().reset_index()
107
- day_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
108
- weekly_pattern['day_of_week'] = pd.Categorical(weekly_pattern['day_of_week'], categories=day_order, ordered=True)
109
- weekly_pattern = weekly_pattern.sort_values('day_of_week')
110
 
111
- plots['weekly'] = px.bar(
112
- weekly_pattern, x='day_of_week', y='weight_kg', color='material_type',
113
- title='Weekly Production Pattern'
114
- )
115
 
116
- # Correlation matrix
117
- daily_pivot = df.groupby(['date', 'material_type'])['weight_kg'].sum().unstack(fill_value=0)
118
- if len(daily_pivot.columns) > 1:
119
- corr_matrix = daily_pivot.corr()
120
- plots['correlation'] = px.imshow(
121
- corr_matrix, title='Material Type Correlation Matrix',
122
- color_continuous_scale='RdBu'
123
- )
124
-
125
- return plots
126
-
127
- def query_llm(model, data_summary, user_question):
128
- """Query Gemini with production data context"""
129
- context = f"""
130
- You are a production data analyst. Here's the current production data summary:
131
-
132
- Production Overview:
133
- - Total Production: {data_summary['total_production']:,.0f} kg
134
- - Production Period: {data_summary['date_range']}
135
- - Daily Average: {data_summary['daily_avg']:,.0f} kg
136
- - Production Days: {data_summary['production_days']}
137
-
138
- Material Breakdown:
139
- """
140
-
141
- for material, stats in data_summary['materials'].items():
142
- context += f"- {material.title()}: {stats['total']:,.0f} kg ({stats['percentage']:.1f}%)\n"
143
-
144
- context += f"\nUser Question: {user_question}\n\nPlease provide a concise, data-driven answer based on this production data."
145
 
146
  try:
147
  response = model.generate_content(context)
148
  return response.text
149
- except Exception as e:
150
- return f"Error querying AI: {str(e)}"
151
 
152
  # Main app
153
  def main():
154
- st.title("🏭 Production Data Analysis Dashboard")
155
- st.markdown("Upload your production data and get AI-powered insights")
156
 
157
- # Initialize Gemini
158
- model = init_gemini()
159
 
160
- # Sidebar
161
  with st.sidebar:
162
- st.header("πŸ“Š Data Upload")
163
- uploaded_file = st.file_uploader("Choose CSV file", type=['csv'])
164
 
165
  if model:
166
- st.success("πŸ€– AI Assistant Ready")
167
  else:
168
- st.warning("⚠️ AI Assistant unavailable (API key needed)")
169
 
170
- if uploaded_file is not None:
171
- # Load and process data
172
- try:
173
- df = pd.read_csv(uploaded_file, sep='\t')
174
- df = process_data(df)
175
-
176
- # Generate analysis
177
- summary = generate_summary(df)
178
- anomalies = detect_anomalies(df)
179
- plots = create_plots(df)
180
-
181
- # Display results
182
- col1, col2, col3, col4 = st.columns(4)
183
-
184
- with col1:
185
- st.metric("Total Production", f"{summary['total_production']:,.0f} kg")
186
- with col2:
187
- st.metric("Daily Average", f"{summary['daily_avg']:,.0f} kg")
188
- with col3:
189
- st.metric("Production Days", summary['production_days'])
190
- with col4:
191
- st.metric("Material Types", len(summary['materials']))
192
-
193
- # Charts
194
- st.subheader("πŸ“ˆ Production Trends")
195
- col1, col2 = st.columns(2)
196
-
197
- with col1:
198
- st.plotly_chart(plots['overview'], use_container_width=True)
199
- with col2:
200
- st.plotly_chart(plots['materials'], use_container_width=True)
201
-
202
- col3, col4 = st.columns(2)
203
- with col3:
204
- st.plotly_chart(plots['weekly'], use_container_width=True)
205
- with col4:
206
- if 'correlation' in plots:
207
- st.plotly_chart(plots['correlation'], use_container_width=True)
208
-
209
- # Material breakdown
210
- st.subheader("πŸ“‹ Material Analysis")
211
- for material, stats in summary['materials'].items():
212
- with st.expander(f"{material.title()} - {stats['total']:,.0f} kg ({stats['percentage']:.1f}%)"):
213
- col1, col2, col3 = st.columns(3)
214
- with col1:
215
- st.metric("Total Weight", f"{stats['total']:,.0f} kg")
216
- with col2:
217
- st.metric("Percentage", f"{stats['percentage']:.1f}%")
218
- with col3:
219
- st.metric("Records", stats['count'])
220
-
221
- # Anomaly detection
222
- st.subheader("⚠️ Anomaly Detection")
223
- for material, anom in anomalies.items():
224
- if anom['count'] > 0:
225
- st.warning(f"**{material.title()}**: {anom['count']} anomalies detected")
226
- st.caption(f"Normal range: {anom['normal_range']}")
227
- if anom['dates']:
228
- st.caption(f"Recent anomaly dates: {', '.join(anom['dates'])}")
229
  else:
230
- st.success(f"**{material.title()}**: No anomalies detected")
 
 
 
 
 
 
 
 
 
 
 
231
 
232
- # AI Chat Interface
233
- if model:
234
- st.subheader("πŸ€– AI Production Assistant")
235
-
236
- # Predefined questions
237
- st.markdown("**Quick Questions:**")
238
- quick_questions = [
239
- "What are the key production trends?",
240
- "Which material type shows the best consistency?",
241
- "Are there any concerning patterns in the data?",
242
- "What recommendations do you have for optimization?"
243
- ]
244
-
245
- cols = st.columns(2)
246
- for i, question in enumerate(quick_questions):
247
- with cols[i % 2]:
248
- if st.button(question, key=f"q_{i}"):
249
- with st.spinner("AI analyzing..."):
250
- answer = query_llm(model, summary, question)
251
- st.success(f"**Q:** {question}")
252
- st.write(f"**A:** {answer}")
253
-
254
- # Custom question
255
- st.markdown("**Ask a Custom Question:**")
256
- user_question = st.text_input("Your question about the production data:")
257
-
258
- if user_question and st.button("Get AI Answer"):
259
- with st.spinner("AI analyzing..."):
260
- answer = query_llm(model, summary, user_question)
261
- st.success(f"**Q:** {user_question}")
262
- st.write(f"**A:** {answer}")
263
 
264
- except Exception as e:
265
- st.error(f"Error processing file: {str(e)}")
266
- st.info("Please ensure your CSV file has columns: date, weight_kg, material_type")
 
 
 
 
267
 
268
  else:
269
- st.info("πŸ‘† Please upload a CSV file to begin analysis")
270
-
271
  st.markdown("""
272
- ### πŸ“‹ Data Format Requirements
273
- Your CSV file should contain:
274
- - `date`: Date in MM/DD/YYYY format
275
- - `weight_kg`: Production weight in kilograms
276
- - `material_type`: Type of material (liquid, solid, waste_water, etc.)
277
- - `shift`: Shift number (optional)
278
-
279
- The file should be tab-separated (TSV format with .csv extension).
280
  """)
281
 
282
  if __name__ == "__main__":
 
3
  import numpy as np
4
  import plotly.express as px
5
  import plotly.graph_objects as go
6
+ from datetime import datetime, timedelta
7
  import google.generativeai as genai
 
8
 
9
  # Page config
10
+ st.set_page_config(page_title="Production Monitor", page_icon="🏭", layout="wide")
 
 
 
 
11
 
 
12
  @st.cache_resource
13
+ def init_ai():
14
  api_key = st.secrets.get("GOOGLE_API_KEY", "")
15
  if api_key:
16
  genai.configure(api_key=api_key)
17
  return genai.GenerativeModel('gemini-1.5-flash')
18
  return None
19
 
 
20
  @st.cache_data
21
+ def load_data(file):
22
+ df = pd.read_csv(file, sep='\t')
23
  df['date'] = pd.to_datetime(df['date'], format='%m/%d/%Y')
24
+ df['day_name'] = df['date'].dt.day_name()
 
 
 
25
  return df
26
 
27
+ def get_material_stats(df):
28
+ stats = {}
29
+ total = df['weight_kg'].sum()
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  for material in df['material_type'].unique():
31
+ data = df[df['material_type'] == material]
32
+ daily_avg = data.groupby('date')['weight_kg'].sum().mean()
33
+
34
+ stats[material] = {
35
+ 'total': data['weight_kg'].sum(),
36
+ 'percentage': (data['weight_kg'].sum() / total) * 100,
37
+ 'daily_avg': daily_avg,
38
+ 'records': len(data)
39
  }
40
+ return stats
41
+
42
+ def create_trend_chart(df, time_period='daily', material_filter=None):
43
+ if material_filter:
44
+ df = df[df['material_type'].isin(material_filter)]
45
+
46
+ if time_period == 'daily':
47
+ grouped = df.groupby(['date', 'material_type'])['weight_kg'].sum().reset_index()
48
+ fig = px.line(grouped, x='date', y='weight_kg', color='material_type',
49
+ title='Daily Production Trend')
50
+ elif time_period == 'shift':
51
+ grouped = df.groupby(['date', 'shift', 'material_type'])['weight_kg'].sum().reset_index()
52
+ fig = px.bar(grouped, x='date', y='weight_kg', color='shift',
53
+ facet_col='material_type', title='Production by Shift')
54
+ else: # weekly
55
+ df['week'] = df['date'].dt.isocalendar().week
56
+ grouped = df.groupby(['week', 'material_type'])['weight_kg'].sum().reset_index()
57
+ fig = px.bar(grouped, x='week', y='weight_kg', color='material_type',
58
+ title='Weekly Production')
59
+
60
+ fig.update_layout(height=400)
61
+ return fig
62
 
63
+ def detect_outliers(df):
64
+ outliers = {}
 
65
  for material in df['material_type'].unique():
66
+ data = df[df['material_type'] == material]['weight_kg']
67
+ Q1, Q3 = data.quantile(0.25), data.quantile(0.75)
 
68
  IQR = Q3 - Q1
69
+ lower, upper = Q1 - 1.5 * IQR, Q3 + 1.5 * IQR
 
70
 
71
+ outlier_count = len(data[(data < lower) | (data > upper)])
72
+ outliers[material] = {
73
+ 'count': outlier_count,
74
+ 'range': f"{lower:.0f} - {upper:.0f} kg"
 
 
 
75
  }
76
+ return outliers
 
77
 
78
+ def query_ai(model, stats, question):
79
+ if not model:
80
+ return "AI assistant not available"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
+ context = f"""Production Data Summary:
83
+ {chr(10).join([f"- {mat.title()}: {info['total']:,.0f}kg ({info['percentage']:.1f}%)"
84
+ for mat, info in stats.items()])}
 
85
 
86
+ Question: {question}
87
+ Answer concisely based on the data:"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
 
89
  try:
90
  response = model.generate_content(context)
91
  return response.text
92
+ except:
93
+ return "Error getting AI response"
94
 
95
  # Main app
96
  def main():
97
+ st.title("🏭 Production Monitor")
98
+ st.markdown("*Real-time production analysis dashboard*")
99
 
100
+ model = init_ai()
 
101
 
102
+ # Sidebar controls
103
  with st.sidebar:
104
+ st.header("πŸ“Š Controls")
105
+ uploaded_file = st.file_uploader("Upload Data", type=['csv'])
106
 
107
  if model:
108
+ st.success("πŸ€– AI Ready")
109
  else:
110
+ st.warning("⚠️ AI Unavailable")
111
 
112
+ if uploaded_file:
113
+ df = load_data(uploaded_file)
114
+ stats = get_material_stats(df)
115
+
116
+ # Material cards
117
+ st.subheader("πŸ“‹ Material Overview")
118
+ cols = st.columns(len(stats))
119
+
120
+ for i, (material, info) in enumerate(stats.items()):
121
+ with cols[i]:
122
+ st.metric(
123
+ label=material.replace('_', ' ').title(),
124
+ value=f"{info['total']:,.0f} kg",
125
+ delta=f"{info['percentage']:.1f}% of total"
126
+ )
127
+ st.caption(f"Daily avg: {info['daily_avg']:,.0f} kg")
128
+
129
+ # Chart controls
130
+ st.subheader("πŸ“ˆ Trends")
131
+ col1, col2 = st.columns([3, 1])
132
+
133
+ with col2:
134
+ time_view = st.selectbox("Time View", ["daily", "weekly", "shift"])
135
+ materials = st.multiselect(
136
+ "Materials",
137
+ options=list(stats.keys()),
138
+ default=list(stats.keys())
139
+ )
140
+
141
+ with col1:
142
+ if materials:
143
+ chart = create_trend_chart(df, time_view, materials)
144
+ st.plotly_chart(chart, use_container_width=True)
145
+
146
+ # Shift analysis
147
+ if 'shift' in df.columns:
148
+ st.subheader("πŸŒ“ Shift Analysis")
149
+ shift_data = df.groupby(['shift', 'material_type'])['weight_kg'].sum().reset_index()
150
+ shift_chart = px.bar(shift_data, x='shift', y='weight_kg', color='material_type',
151
+ title='Production by Shift')
152
+ st.plotly_chart(shift_chart, use_container_width=True)
153
+
154
+ # Anomaly detection
155
+ st.subheader("⚠️ Quality Check")
156
+ outliers = detect_outliers(df)
157
+
158
+ alert_cols = st.columns(len(outliers))
159
+ for i, (material, info) in enumerate(outliers.items()):
160
+ with alert_cols[i]:
161
+ if info['count'] > 0:
162
+ st.warning(f"**{material.title()}**: {info['count']} outliers")
163
+ st.caption(f"Normal: {info['range']}")
 
 
 
 
 
 
 
164
  else:
165
+ st.success(f"**{material.title()}**: All normal")
166
+
167
+ # AI Assistant
168
+ if model:
169
+ st.subheader("πŸ€– AI Insights")
170
+
171
+ # Quick questions
172
+ quick_q = [
173
+ "What's the production trend?",
174
+ "Which material is most consistent?",
175
+ "Any efficiency recommendations?"
176
+ ]
177
 
178
+ cols = st.columns(len(quick_q))
179
+ for i, q in enumerate(quick_q):
180
+ with cols[i]:
181
+ if st.button(q, key=f"q{i}"):
182
+ answer = query_ai(model, stats, q)
183
+ st.info(answer)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
 
185
+ # Custom question
186
+ custom_q = st.text_input("Ask anything about your data:")
187
+ if custom_q:
188
+ if st.button("Ask"):
189
+ answer = query_ai(model, stats, custom_q)
190
+ st.success(f"**Q:** {custom_q}")
191
+ st.write(f"**A:** {answer}")
192
 
193
  else:
194
+ st.info("πŸ“ Upload your production data to start")
 
195
  st.markdown("""
196
+ **Expected format (TSV):**
197
+ - `date`: MM/DD/YYYY
198
+ - `weight_kg`: Production weight
199
+ - `material_type`: Material category
200
+ - `shift`: day/night (optional)
 
 
 
201
  """)
202
 
203
  if __name__ == "__main__":