mgbam commited on
Commit
0a40e29
·
verified ·
1 Parent(s): 63a9cd3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +179 -193
app.py CHANGED
@@ -1,203 +1,189 @@
1
- # app.py — BizIntel AI Ultra
2
- # Supports: CSV/Excel/DB ingestion, date+metric plotting, ARIMA forecasting,
3
- # safe Plotly writes into /tmp, Gemini 1.5 Pro strategy, KPI cards, optional EDA.
4
-
5
- import os
6
- import tempfile
7
-
8
- import pandas as pd
9
  import streamlit as st
10
- import google.generativeai as genai
11
- import plotly.graph_objects as go
12
-
13
- # ──────────────────────────────────────────────────────────────
14
- # 0) Monkey‐patch Plotly to write images into /tmp (writable)
15
- # ──────────────────────────────────────────────────────────────
16
- TMP = tempfile.gettempdir()
17
- _orig_write = go.Figure.write_image
18
- def _safe_write(self, path, *args, **kwargs):
19
- filename = os.path.basename(path)
20
- safe_path = os.path.join(TMP, filename)
21
- return _orig_write(self, safe_path, *args, **kwargs)
22
- go.Figure.write_image = _safe_write
23
-
24
- # ──────────────────────────────────────────────────────────────
25
- # 1) Tool & DB imports
26
- # ──────────────────────────────────────────────────────────────
27
- from tools.csv_parser import parse_csv_tool
28
- from tools.plot_generator import plot_metric_tool
29
- from tools.forecaster import forecast_metric_tool
30
- from tools.visuals import histogram_tool, scatter_matrix_tool, corr_heatmap_tool
31
- from db_connector import fetch_data_from_db, list_tables, SUPPORTED_ENGINES
32
-
33
- # ──────────────────────────────────────────────────────────────
34
- # 2) Gemini 1.5 Pro initialization
35
- # ──────────────────────────────────────────────────────────────
36
- genai.configure(api_key=os.getenv("GEMINI_APIKEY"))
37
- gemini = genai.GenerativeModel(
38
- "gemini-1.5-pro-latest",
39
- generation_config={
40
- "temperature": 0.7,
41
- "top_p": 0.9,
42
- "response_mime_type": "text/plain",
43
- },
 
 
 
 
44
  )
45
-
46
- # ──────────────────────────────────────────────────────────────
47
- # 3) Streamlit page setup
48
- # ──────────────────────────────────────────────────────────────
49
- st.set_page_config(page_title="BizIntel AI Ultra", layout="wide")
50
  st.title("📊 BizIntel AI Ultra – Advanced Analytics + Gemini 1.5 Pro")
51
- TEMP_DIR = tempfile.gettempdir()
52
-
53
- # ──────────────────────────────────────────────────────────────
54
- # 4) Data source selection: CSV/Excel or SQL Database
55
- # ───────────────────────────────────���──────────────────────────
56
- source = st.radio("Select data source", ["Upload CSV / Excel", "Connect to SQL Database"])
57
- csv_path = None
58
-
59
- if source == "Upload CSV / Excel":
60
- upload = st.file_uploader("Upload CSV or Excel (≤ 500 MB)", type=["csv","xlsx","xls"])
61
- if upload:
62
- tmp_file = os.path.join(TEMP_DIR, upload.name)
63
- with open(tmp_file, "wb") as f:
64
- f.write(upload.read())
65
-
66
- if upload.name.lower().endswith(".csv"):
67
- csv_path = tmp_file
68
- else:
69
- try:
70
- df_xl = pd.read_excel(tmp_file, sheet_name=0)
71
- csv_path = tmp_file.rsplit(".", 1)[0] + ".csv"
72
- df_xl.to_csv(csv_path, index=False)
73
- except Exception as e:
74
- st.error(f"Excel parsing failed: {e}")
75
- st.stop()
76
- st.success(f"{upload.name} saved ✅")
77
-
78
- else:
79
- engine = st.selectbox("DB engine", SUPPORTED_ENGINES)
80
- conn = st.text_input("SQLAlchemy connection string")
81
- if conn:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  try:
83
- tables = list_tables(conn)
84
- tbl = st.selectbox("Table", tables)
85
- if st.button("Fetch table"):
86
- csv_path = fetch_data_from_db(conn, tbl)
87
- st.success(f"Fetched **{tbl}** as CSV ✅")
 
 
 
 
 
 
88
  except Exception as e:
89
- st.error(f"Connection failed: {e}")
90
- st.stop()
91
-
92
- if not csv_path:
93
- st.stop()
94
-
95
- # Download the working CSV
96
- with open(csv_path, "rb") as f:
97
- st.download_button("⬇️ Download working CSV", f, file_name=os.path.basename(csv_path))
98
-
99
- # ──────────────────────────────────────────────────────────────
100
- # 5) Show head & pick date + metric (but never the same column)
101
- # ──────────────────────────────────────────────────────────────
102
- df_head = pd.read_csv(csv_path, nrows=5)
103
- st.dataframe(df_head)
104
-
105
- # a) Date dropdown over all columns
106
- date_col = st.selectbox("Select date/time column", df_head.columns)
107
-
108
- # b) Metric dropdown only numeric columns, excluding the chosen date_col
109
- numeric_cols = df_head.select_dtypes("number").columns.tolist()
110
- metric_options = [c for c in numeric_cols if c != date_col]
111
- if not metric_options:
112
- st.error(f"No numeric columns available once we exclude '{date_col}'.")
113
- st.stop()
114
- metric_col = st.selectbox("Select numeric metric column", metric_options)
115
-
116
- # ──────────────────────────────────────────────────────────────
117
- # 6) Local analysis: summary, trend chart, forecast
118
- # ──────────────────────────────────────────────────────────────
119
- with st.spinner("Parsing dataset…"):
120
- summary_text = parse_csv_tool(csv_path)
121
-
122
- with st.spinner("Generating trend chart…"):
123
- trend_fig = plot_metric_tool(csv_path, date_col, metric_col)
124
- if isinstance(trend_fig, go.Figure):
125
- st.subheader("📈 Trend")
126
- st.plotly_chart(trend_fig, use_container_width=True)
127
- else:
128
- st.warning(trend_fig)
129
-
130
- with st.spinner("Running forecast…"):
131
- forecast_text = forecast_metric_tool(csv_path, date_col, metric_col)
132
-
133
- st.subheader(f"🔮 {metric_col} Forecast")
134
- forecast_png = os.path.join(TEMP_DIR, "forecast_plot.png")
135
- if os.path.exists(forecast_png):
136
- st.image(forecast_png, use_container_width=True)
137
- else:
138
- st.warning("Forecast image not found.")
139
-
140
- # ──────────────────────────────────────────────────────────────
141
- # 7) Gemini-driven strategy recommendations
142
- # ──────────────────────────────────────────────────────────────
143
- prompt = (
144
- f"You are **BizIntel Strategist AI**.\n\n"
145
- f"### Dataset Summary\n```\n{summary_text}\n```\n\n"
146
- f"### {metric_col} Forecast\n```\n{forecast_text}\n```\n\n"
147
- "Return **Markdown** with:\n"
148
- "1. Five key insights\n"
149
- "2. Three actionable strategies\n"
150
- "3. Risk factors or anomalies\n"
151
- "4. Suggested additional visuals\n"
152
- )
153
 
154
- st.subheader("🚀 Strategy Recommendations (Gemini 1.5 Pro)")
155
- with st.spinner("Generating insights…"):
156
- strategy_md = gemini.generate_content(prompt).text
157
- st.markdown(strategy_md)
158
- st.download_button("⬇️ Download Strategy (.md)", strategy_md, file_name="strategy.md")
159
-
160
- # ──────────────────────────────────────────────────────────────
161
- # 8) KPI cards + detailed Stats
162
- # ────────────────────────────────────────────────────────��─────
163
- full_df = pd.read_csv(csv_path, low_memory=False)
164
- total_rows = len(full_df)
165
- num_columns = full_df.shape[1]
166
- missing_pct = full_df.isna().mean().mean() * 100
167
-
168
- st.markdown("---")
169
- st.subheader("📑 Dataset Overview")
170
- c1, c2, c3 = st.columns(3)
171
- c1.metric("Rows", f"{total_rows:,}")
172
- c2.metric("Columns", str(num_columns))
173
- c3.metric("Missing %", f"{missing_pct:.1f}%")
174
-
175
- with st.expander("🔎 Detailed descriptive statistics"):
176
- stats_df = (
177
- full_df.describe()
178
- .T
179
- .reset_index()
180
- .rename(columns={"index":"Feature"})
181
- )
182
- st.dataframe(
183
- stats_df.style.format(precision=2).background_gradient(cmap="Blues"),
184
- use_container_width=True
185
- )
186
 
187
- # ──────────────────────────────────────────────────────────────
188
- # 9) Optional Exploratory Visuals
189
- # ──────────────────────────────────────────────────────────────
190
- st.markdown("---")
191
- st.subheader("🔍 Optional Exploratory Visuals")
192
 
193
- if st.checkbox("Histogram"):
194
- hcol = st.selectbox("Variable", numeric_cols, key="hist")
195
- st.plotly_chart(histogram_tool(csv_path, hcol), use_container_width=True)
196
 
197
- if st.checkbox("Scatter Matrix"):
198
- sel = st.multiselect("Choose columns", numeric_cols, default=numeric_cols[:3])
199
- if sel:
200
- st.plotly_chart(scatter_matrix_tool(csv_path, sel), use_container_width=True)
201
 
202
- if st.checkbox("Correlation Heatmap"):
203
- st.plotly_chart(corr_heatmap_tool(csv_path), use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
+ import pandas as pd
3
+ import numpy as np
4
+ import tempfile
5
+ from io import BytesIO
6
+ from sqlalchemy import create_engine
7
+ import plotly.express as px
8
+ import matplotlib.pyplot as plt
9
+ from statsmodels.tsa.arima.model import ARIMA
10
+
11
+ # ── Helpers to read CSV/Excel robustly ───────────────────────────────────────────
12
+ @st.cache_data
13
+ def load_file(uploaded):
14
+ """Read a CSV or Excel file into a DataFrame."""
15
+ try:
16
+ if uploaded.name.lower().endswith((".xls", ".xlsx")):
17
+ return pd.read_excel(uploaded, engine="openpyxl")
18
+ else:
19
+ return pd.read_csv(uploaded)
20
+ except Exception as e:
21
+ raise st.Error(f"Error parsing file: {e}")
22
+
23
+ # ── Helpers for SQL database ────────────────────────────────────────────────────
24
+ SUPPORTED_ENGINES = ["postgresql", "mysql", "mssql+pyodbc", "oracle+cx_oracle"]
25
+ @st.cache_data
26
+ def list_tables(connection_string):
27
+ engine = create_engine(connection_string)
28
+ return engine.table_names()
29
+
30
+ @st.cache_data
31
+ def fetch_table(connection_string, table_name):
32
+ engine = create_engine(connection_string)
33
+ return pd.read_sql_table(table_name, engine)
34
+
35
+ # ── Streamlit page setup ────────────────────────────────────────────────────────
36
+ st.set_page_config(
37
+ page_title="BizIntel AI Ultra",
38
+ layout="wide",
39
+ initial_sidebar_state="expanded",
40
  )
 
 
 
 
 
41
  st.title("📊 BizIntel AI Ultra – Advanced Analytics + Gemini 1.5 Pro")
42
+
43
+ # ── Data source selection ───────────────────────────────────────────────────────
44
+ data_source = st.radio("Select data source", ["Upload CSV / Excel", "Connect to SQL Database"])
45
+
46
+ df = None
47
+ if data_source == "Upload CSV / Excel":
48
+ uploaded = st.file_uploader(
49
+ "Drag & drop file here (≤ 500 MB)",
50
+ type=["csv", "xls", "xlsx"],
51
+ accept_multiple_files=False,
52
+ )
53
+ if uploaded:
54
+ with st.spinner("Loading file…"):
55
+ df = load_file(uploaded)
56
+ st.success("✅ File loaded into memory")
57
+ elif data_source == "Connect to SQL Database":
58
+ engine = st.selectbox("Select DB engine", SUPPORTED_ENGINES)
59
+ conn_str = st.text_input("Connection string (SQLAlchemy format)", placeholder="e.g. postgresql://user:pass@host:port/dbname")
60
+ if conn_str:
61
+ tables = list_tables(conn_str)
62
+ table = st.selectbox("Choose table", tables)
63
+ if table:
64
+ with st.spinner(f"Fetching `{table}`…"):
65
+ df = fetch_table(conn_str, table)
66
+ st.success(f"✅ `{table}` loaded from database")
67
+
68
+ # ── If DataFrame is ready, show overview and proceed ───────────────────────────
69
+ if df is not None:
70
+ st.markdown("### 🗂️ Preview")
71
+ st.dataframe(df.head(5), use_container_width=True)
72
+
73
+ # Dataset overview metrics
74
+ n_rows, n_cols = df.shape
75
+ missing_pct = (df.isna().sum().sum() / (n_rows * n_cols)) * 100
76
+ st.markdown("---")
77
+ c1, c2, c3 = st.columns(3)
78
+ c1.metric("Rows", f"{n_rows:,}")
79
+ c2.metric("Columns", f"{n_cols:,}")
80
+ c3.metric("Missing %", f"{missing_pct:.1f}%")
81
+
82
+ # Detailed stats
83
+ st.markdown("#### 📋 Detailed descriptive statistics")
84
+ st.dataframe(df.describe(include="all").transpose(), use_container_width=True)
85
+
86
+ # Optional exploratory visuals
87
+ st.markdown("---")
88
+ st.markdown("#### 🔎 Optional Exploratory Visuals")
89
+ col1, col2, col3 = st.columns(3)
90
+ with col1:
91
+ if st.checkbox("Histogram"):
92
+ num_cols = df.select_dtypes(include="number").columns.tolist()
93
+ col = st.selectbox("Choose numeric column for histogram", num_cols, key="hist")
94
+ fig = px.histogram(df, x=col, nbins=30, title=f"Histogram of {col}")
95
+ st.plotly_chart(fig, use_container_width=True)
96
+ with col2:
97
+ if st.checkbox("Scatter matrix"):
98
+ num_cols = df.select_dtypes(include="number").columns.tolist()[:6] # limit to first 6
99
+ fig = px.scatter_matrix(df[num_cols], dimensions=num_cols, title="Scatter Matrix")
100
+ st.plotly_chart(fig, use_container_width=True)
101
+ with col3:
102
+ if st.checkbox("Correlation heatmap"):
103
+ corr = df.select_dtypes(include="number").corr()
104
+ fig, ax = plt.subplots(figsize=(6, 5))
105
+ im = ax.imshow(corr, vmin=-1, vmax=1, cmap="RdBu")
106
+ plt.xticks(range(len(corr)), corr.columns, rotation=45, ha="right")
107
+ plt.yticks(range(len(corr)), corr.columns)
108
+ plt.colorbar(im, ax=ax)
109
+ st.pyplot(fig)
110
+
111
+ # ── Trend & Forecast ──────────────────────────────────────────────────────
112
+ st.markdown("---")
113
+ st.markdown("### 📈 Trend & Forecast")
114
+ # pick date/time column
115
+ dt_cols = df.columns[df.dtypes.isin([np.dtype("datetime64[ns]"), np.dtype("object")])].tolist()
116
+ date_col = st.selectbox("Select date/time column", dt_cols)
117
+ df[date_col] = pd.to_datetime(df[date_col], errors="coerce")
118
+
119
+ # pick numeric metric
120
+ num_cols = df.select_dtypes(include="number").columns.tolist()
121
+ metric_col = st.selectbox("Select numeric metric", num_cols)
122
+
123
+ # prepare time series
124
+ ts = df[[date_col, metric_col]].dropna()
125
+ ts = ts.set_index(date_col).sort_index()
126
+ ts = ts[~ts.index.duplicated(keep="first")]
127
+
128
+ # Trend plot
129
+ fig_trend = px.line(ts, y=metric_col, title=f"{metric_col} over Time")
130
+ st.plotly_chart(fig_trend, use_container_width=True)
131
+
132
+ # Forecast next 90 days with ARIMA
133
+ with st.spinner("Running 90-day forecast…"):
134
  try:
135
+ model = ARIMA(ts, order=(1, 1, 1)).fit()
136
+ fcast = model.get_forecast(90)
137
+ idx = pd.date_range(ts.index.max(), periods=91, freq="D")[1:]
138
+ df_f = pd.DataFrame({"forecast": fcast.predicted_mean}, index=idx)
139
+
140
+ fig_fc = px.line(
141
+ pd.concat([ts, df_f], axis=1),
142
+ labels={metric_col: metric_col, "forecast": "Forecast"},
143
+ title=f"{metric_col} & 90-Day Forecast",
144
+ )
145
+ st.plotly_chart(fig_fc, use_container_width=True)
146
  except Exception as e:
147
+ st.error(f"Forecast failed: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
 
149
+ # ── Strategy Recommendations ─────────────────────────────────────────────
150
+ st.markdown("---")
151
+ st.markdown("### 🚀 Strategy Recommendations")
152
+ st.markdown(
153
+ """
154
+ 1. **Data Quality First**
155
+ Address any missing or malformed dates before further time-series analysis.
156
+
157
+ 2. **Trend & Seasonality**
158
+ Investigate any upward/downward trends and repeating seasonal patterns.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
 
160
+ 3. **Outlier Management**
161
+ Identify extreme highs/lows in your metric—could be bulk orders or data errors.
 
 
 
162
 
163
+ 4. **Segment-Level Analysis**
164
+ Drill into key dimensions (e.g. region, product) to tailor growth strategies.
 
165
 
166
+ 5. **Predict & Act**
167
+ Use your 90-day forecasts to guide inventory, staffing, and marketing decisions.
168
+ """
169
+ )
170
 
171
+ # downloadable strategy as markdown
172
+ strategy_md = st.session_state.get("strategy_md", "")
173
+ if not strategy_md:
174
+ strategy_md = st.session_state["strategy_md"] = st.container().markdown("…") # dummy to store
175
+
176
+ st.download_button(
177
+ "📥 Download Strategy (.md)",
178
+ data="""
179
+ # BizIntel AI Ultra – Strategy Recommendations
180
+
181
+ 1. Data Quality First: …
182
+ 2. Trend & Seasonality: …
183
+ 3. Outlier Management: …
184
+ 4. Segment-Level Analysis: …
185
+ 5. Predict & Act: …
186
+ """,
187
+ file_name="strategy.md",
188
+ mime="text/markdown",
189
+ )