mgbam commited on
Commit
a875537
ยท
verified ยท
1 Parent(s): b5d6aaa

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +94 -88
app.py CHANGED
@@ -1,39 +1,51 @@
1
- """app.py โ€” BizIntel AI Ultra (Geminiโ€‘only, v4)
2
- A productionโ€‘grade BI copilot with:
3
- โ€ข CSVโ€ฏ/โ€ฏExcelโ€ฏ/โ€ฏParquet and SQL ingestion
4
- โ€ข Smart sampling + memoryโ€‘safe loading for large files
5
- โ€ข Schema + missing-data audit with Gemini-generated insights
6
- โ€ข Drill-down EDA (histogram, violin, scatter-matrix, heatmap)
7
- โ€ข Autoโ€‘detected date column, tunable ARIMA forecasting
8
- โ€ข One-click strategy brief download (Markdown)
9
  """
10
-
11
  from __future__ import annotations
12
- import os, io, tempfile
13
  from pathlib import Path
14
  from typing import List
15
 
16
  import pandas as pd
17
- import streamlit as st
18
  import plotly.express as px
19
- from statsmodels.tsa.arima.model import ARIMA
20
  from sqlalchemy import create_engine
 
21
  import google.generativeai as genai
22
 
23
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 0โ€ฏยทโ€ฏCONFIGURATION โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
24
- API_KEY = st.secrets.get("GEMINI_APIKEY") or os.getenv("GEMINI_APIKEY")
25
- if not API_KEY:
26
- st.error("โŒ Missing `GEMINI_APIKEY` โ€” add it in Settings โ†’ Secrets or set env variable.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  st.stop()
28
 
29
- st.set_page_config("BizIntelย AIย Ultra", "๐Ÿ“Š", "wide", initial_sidebar_state="expanded")
30
- genai.configure(api_key=API_KEY)
31
  GEM_MODEL = "gemini-1.5-pro-latest"
32
  TMP = Path(tempfile.gettempdir())
33
 
34
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 1โ€ฏยทโ€ฏUTILITY HELPERS โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
35
  @st.cache_data(show_spinner=False)
36
- def read_file(buf: io.BufferedReader, sample: bool = False) -> pd.DataFrame:
37
  suf = Path(buf.name).suffix.lower()
38
  if suf in {".xls", ".xlsx"}:
39
  return pd.read_excel(buf, engine="openpyxl")
@@ -42,105 +54,99 @@ def read_file(buf: io.BufferedReader, sample: bool = False) -> pd.DataFrame:
42
  return pd.read_csv(buf, nrows=5_000_000 if sample else None)
43
 
44
  @st.cache_data(show_spinner=False)
45
- def sql_tables(uri: str) -> List[str]:
46
  return create_engine(uri).table_names()
47
 
48
  @st.cache_data(show_spinner=True)
49
- def read_table(uri: str, tbl: str) -> pd.DataFrame:
50
  return pd.read_sql_table(tbl, create_engine(uri))
51
 
52
  @st.cache_data(show_spinner=False)
53
- def ask_gemini(prompt: str) -> str:
54
  return genai.GenerativeModel(GEM_MODEL).generate_content(prompt).text.strip()
55
 
56
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 2โ€ฏยทโ€ฏDATA INGESTION โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
57
- st.title("๐Ÿ“Š BizIntel AI Ultra โ€” Gemini 1.5 Pro BI Copilot")
58
- mode = st.sidebar.radio("Select Data Source", ["Upload File", "SQL Database"], horizontal=True)
59
- df: pd.DataFrame = pd.DataFrame()
60
 
61
- if mode == "Upload File":
62
- upl = st.sidebar.file_uploader("Upload CSV / Excel / Parquet", ["csv", "xls", "xlsx", "parquet"], help="โ‰ค2โ€ฏGB")
63
- sample = st.sidebar.checkbox("Load sample (โ‰ค 5M rows)")
 
64
  if upl:
65
- df = read_file(upl, sample)
66
  else:
67
  uri = st.sidebar.text_input("SQLAlchemy URI")
68
  if uri:
69
- tbl = st.sidebar.selectbox("Choose Table", sql_tables(uri))
70
  if tbl:
71
- df = read_table(uri, tbl)
72
 
73
  if df.empty:
74
- st.info("โฌ…๏ธ Load a dataset to get started.")
75
  st.stop()
76
 
77
- st.success("โœ… Data loaded")
78
  st.dataframe(df.head(), use_container_width=True)
79
 
80
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 3โ€ฏยทโ€ฏSUMMARY + GEMINI โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
81
  rows, cols = df.shape
82
- miss_pct = df.isna().sum().sum() / (rows * cols) * 100
83
- c1, c2, c3 = st.columns(3)
84
- c1.metric("Rows", f"{rows:,}")
85
- c2.metric("Columns", cols)
86
- c3.metric("Missing %", f"{miss_pct:.1f}")
87
-
88
- st.subheader("๐Ÿง  Gemini Insights")
89
- with st.spinner("Generating analysis..."):
90
- summary = df.describe(include="all", datetime_is_numeric=True).round(2).to_json()
91
- st.markdown(ask_gemini(
92
- "You are a senior BI analyst. List 5 key insights and 3 action items based on this dataset: " + summary
93
  ))
94
 
95
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 4โ€ฏยทโ€ฏTIME SERIES SETUP โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
96
- # try datetime coercion
97
- for c in df.columns:
98
- if not pd.api.types.is_datetime64_any_dtype(df[c]):
99
  try:
100
- df[c] = pd.to_datetime(df[c])
101
  except: pass
102
 
103
  DATE_COL = st.selectbox("Date column", [c for c in df.columns if pd.api.types.is_datetime64_any_dtype(df[c])])
104
- METRIC_COL = st.selectbox("Numeric metric", [c for c in df.select_dtypes("number").columns])
105
-
106
- series = (
107
- df[[DATE_COL, METRIC_COL]].dropna()
108
- .groupby(DATE_COL)[METRIC_COL].mean().sort_index()
109
- )
110
- fig_ts = px.line(series, title=f"{METRIC_COL} Trend", labels={"index": "Date", METRIC_COL: METRIC_COL})
111
- st.plotly_chart(fig_ts, use_container_width=True)
112
-
113
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 5โ€ฏยทโ€ฏARIMA FORECASTING โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
114
- st.subheader("๐Ÿ”ฎ Forecast")
115
- steps = st.slider("Forecast Horizon", 3, 365, 90)
116
- p = st.number_input("AR Order (p)", 0, 5, 1)
117
- d = st.number_input("Diff Order (d)", 0, 2, 1)
118
- q = st.number_input("MA Order (q)", 0, 5, 1)
119
-
120
- with st.spinner("Training ARIMA model..."):
121
- model = ARIMA(series, order=(p, d, q)).fit()
122
- fut_idx = pd.date_range(series.index[-1], periods=steps + 1, freq=pd.infer_freq(series.index) or "D")[1:]
123
- forecast = pd.Series(model.forecast(steps), index=fut_idx)
124
- fig_fc = px.line(pd.concat([series, forecast.rename("Forecast")], axis=1), title="Actual vs Forecast")
125
- st.plotly_chart(fig_fc, use_container_width=True)
126
-
127
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 6โ€ฏยทโ€ฏEDA TOOLS โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
128
- st.subheader("๐Ÿ” Exploratory Data Dashboard")
129
  with st.expander("Histogram + Box"):
130
- col = st.selectbox("Metric column", METRIC_COL, key="hist")
131
- st.plotly_chart(px.histogram(df, x=col, marginal="box", template="plotly_dark"), use_container_width=True)
132
-
133
  with st.expander("Correlation Heatmap"):
134
  corr = df.select_dtypes("number").corr()
135
- st.plotly_chart(px.imshow(corr, color_continuous_scale="RdBu", aspect="auto", title="Correlation Matrix"), use_container_width=True)
136
 
137
- # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 7โ€ฏยทโ€ฏSTRATEGY DOWNLOAD โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
138
  brief = (
139
  "# Strategy Brief\n"
140
- "* Clean missing date values for better time modeling.\n"
141
- "* Investigate top correlations for potential drivers.\n"
142
- "* Leverage forecast for inventory and staff planning.\n"
143
- "* Watch for outliers >3ฯƒ weekly.\n"
144
- "* Segment by region and product for precise actions."
145
  )
146
- st.download_button("โฌ‡๏ธ Download Strategy (.md)", brief, "bizintel_brief.md", "text/markdown")
 
1
+ """app.pyย โ€”ย BizIntelย AIย Ultraย (Geminiโ€‘only,ย v4)
2
+ Robust BI copilot featuring:
3
+ โ€ข CSVย /ย Excelย /ย Parquet uploads + live SQL ingestion
4
+ โ€ข Memoryโ€‘safe sampling for large files (โ‰คโ€ฏ5โ€ฏM rows sample)
5
+ โ€ข Geminiโ€‘generated narrative insights & quick schema audit
6
+ โ€ข Interactive EDA (histogramย +ย box, correlation heatmap)
7
+ โ€ข Tunable ARIMA forecasting (p,d,q & horizon)
8
+ โ€ข Oneโ€‘click strategy brief download
9
  """
 
10
  from __future__ import annotations
11
+ import io, os, tempfile
12
  from pathlib import Path
13
  from typing import List
14
 
15
  import pandas as pd
 
16
  import plotly.express as px
17
+ import streamlit as st
18
  from sqlalchemy import create_engine
19
+ from statsmodels.tsa.arima.model import ARIMA
20
  import google.generativeai as genai
21
 
22
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 0โ€ฏยทโ€ฏPAGE CONFIG โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
23
+ st.set_page_config(
24
+ page_title="BizIntelย AIย Ultra",
25
+ page_icon="๐Ÿ“Š",
26
+ layout="wide",
27
+ initial_sidebar_state="expanded",
28
+ )
29
+
30
+ auth_key = None
31
+ try:
32
+ auth_key = st.secrets["GEMINI_APIKEY"] # type: ignore[attr-defined]
33
+ except Exception:
34
+ auth_key = os.getenv("GEMINI_APIKEY")
35
+
36
+ if not auth_key:
37
+ st.error(
38
+ "โŒ **GEMINI_APIKEY** missing. Add it in *Settings โ†’ Secrets* or set envโ€ฏvar before running."
39
+ )
40
  st.stop()
41
 
42
+ genai.configure(api_key=auth_key)
 
43
  GEM_MODEL = "gemini-1.5-pro-latest"
44
  TMP = Path(tempfile.gettempdir())
45
 
46
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 1โ€ฏยทโ€ฏHELPERS โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
47
  @st.cache_data(show_spinner=False)
48
+ def load_file(buf: io.BufferedReader, sample: bool = False) -> pd.DataFrame:
49
  suf = Path(buf.name).suffix.lower()
50
  if suf in {".xls", ".xlsx"}:
51
  return pd.read_excel(buf, engine="openpyxl")
 
54
  return pd.read_csv(buf, nrows=5_000_000 if sample else None)
55
 
56
  @st.cache_data(show_spinner=False)
57
+ def list_sql_tables(uri: str) -> List[str]:
58
  return create_engine(uri).table_names()
59
 
60
  @st.cache_data(show_spinner=True)
61
+ def fetch_sql_table(uri: str, tbl: str) -> pd.DataFrame:
62
  return pd.read_sql_table(tbl, create_engine(uri))
63
 
64
  @st.cache_data(show_spinner=False)
65
+ def gemini(prompt: str) -> str:
66
  return genai.GenerativeModel(GEM_MODEL).generate_content(prompt).text.strip()
67
 
68
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 2โ€ฏยทโ€ฏDATA LOAD UI โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
69
+ st.title("๐Ÿ“Šย BizIntelย AIย Ultra โ€” Geminiย 1.5ย Pro BI Copilot")
70
+ mode = st.sidebar.radio("Data source", ["Upload file", "SQL DB"], horizontal=True)
 
71
 
72
+ df: pd.DataFrame = pd.DataFrame()
73
+ if mode == "Upload file":
74
+ upl = st.sidebar.file_uploader("CSV / Excel / Parquet", ["csv", "xls", "xlsx", "parquet"], help="โ‰คโ€ฏ2โ€ฏGB")
75
+ sample = st.sidebar.checkbox("Sample firstย 5โ€ฏM rows only")
76
  if upl:
77
+ df = load_file(upl, sample)
78
  else:
79
  uri = st.sidebar.text_input("SQLAlchemy URI")
80
  if uri:
81
+ tbl = st.sidebar.selectbox("Table", list_sql_tables(uri))
82
  if tbl:
83
+ df = fetch_sql_table(uri, tbl)
84
 
85
  if df.empty:
86
+ st.info("โฌ…๏ธย Load data to start analysis.")
87
  st.stop()
88
 
89
+ st.success("โœ…ย Data loaded")
90
  st.dataframe(df.head(), use_container_width=True)
91
 
92
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 3โ€ฏยทโ€ฏOVERVIEW & GEMINI INSIGHT โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
93
  rows, cols = df.shape
94
+ missing_pct = df.isna().sum().sum() / (rows * cols) * 100
95
+ m1,m2,m3 = st.columns(3)
96
+ m1.metric("Rows", f"{rows:,}")
97
+ m2.metric("Columns", cols)
98
+ m3.metric("Missingย %", f"{missing_pct:.1f}")
99
+
100
+ st.subheader("๐Ÿง ย Gemini Insights")
101
+ with st.spinner("Generating narrativeโ€ฆ"):
102
+ summ_json = df.describe(include="all", datetime_is_numeric=True).round(2).to_json()
103
+ st.markdown(gemini(
104
+ "You are a senior BI analyst. Provide five bullet insights and three actions for this dataset:\n\n" + summ_json
105
  ))
106
 
107
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 4โ€ฏยทโ€ฏTIMEโ€‘SERIES PREP โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
108
+ # attempt datetime coercion
109
+ for col in df.columns:
110
+ if not pd.api.types.is_datetime64_any_dtype(df[col]):
111
  try:
112
+ df[col] = pd.to_datetime(df[col])
113
  except: pass
114
 
115
  DATE_COL = st.selectbox("Date column", [c for c in df.columns if pd.api.types.is_datetime64_any_dtype(df[c])])
116
+ NUM_COL = st.selectbox("Numeric metric", [c for c in df.select_dtypes("number").columns])
117
+
118
+ ts = df[[DATE_COL, NUM_COL]].dropna().groupby(DATE_COL)[NUM_COL].mean().sort_index()
119
+ fig_tr = px.line(ts, title=f"{NUM_COL} Trend", labels={"index":"Date", NUM_COL:NUM_COL})
120
+ st.plotly_chart(fig_tr, use_container_width=True)
121
+
122
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 5โ€ฏยทโ€ฏARIMA FORECAST โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
123
+ st.subheader("๐Ÿ”ฎย ARIMA Forecast")
124
+ steps = st.slider("Horizon (days)", 3, 365, 90)
125
+ p = st.number_input("p", 0, 5, 1); d = st.number_input("d", 0, 2, 1); q = st.number_input("q", 0, 5, 1)
126
+ try:
127
+ model = ARIMA(ts, order=(p, d, q)).fit()
128
+ future_idx = pd.date_range(ts.index[-1], periods=steps + 1, freq=pd.infer_freq(ts.index) or "D")[1:]
129
+ forecast = pd.Series(model.forecast(steps), index=future_idx, name="Forecast")
130
+ fig_fc = px.line(pd.concat([ts, forecast], axis=1), title="Actual vs Forecast")
131
+ st.plotly_chart(fig_fc, use_container_width=True)
132
+ except Exception as e:
133
+ st.error(f"ARIMA failed: {e}")
134
+
135
+ # โ”€โ”€โ”€โ”€โ”€โ”€๏ฟฝ๏ฟฝ๏ฟฝโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 6โ€ฏยทโ€ฏEDA EXPANDERS โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
136
+ st.subheader("๐Ÿ”ย EDA Dashboard")
 
 
 
 
137
  with st.expander("Histogram + Box"):
138
+ st.plotly_chart(px.histogram(df, x=NUM_COL, marginal="box", template="plotly_dark"), use_container_width=True)
 
 
139
  with st.expander("Correlation Heatmap"):
140
  corr = df.select_dtypes("number").corr()
141
+ st.plotly_chart(px.imshow(corr, color_continuous_scale="RdBu", title="Correlation"), use_container_width=True)
142
 
143
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 7โ€ฏยทโ€ฏSTRATEGY BRIEF โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
144
  brief = (
145
  "# Strategy Brief\n"
146
+ "โ€ข Clean missing date values for stable models.\n"
147
+ "โ€ข Investigate high correlations for driver analysis.\n"
148
+ "โ€ข Use forecast for inventory & workforce planning.\n"
149
+ "โ€ข Monitor outliers (>3ฯƒ) weekly.\n"
150
+ "โ€ข Segment by region/product for targeted actions."
151
  )
152
+ st.download_button("โฌ‡๏ธย Download Strategy (.md)", brief, "bizintel_brief.md", "text/markdown")