Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,39 +1,51 @@
|
|
1 |
-
"""app.py
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
"""
|
10 |
-
|
11 |
from __future__ import annotations
|
12 |
-
import
|
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 |
-
|
20 |
from sqlalchemy import create_engine
|
|
|
21 |
import google.generativeai as genai
|
22 |
|
23 |
-
# โโโโโโโโโโโโโโโโโโโโโ 0โฏยทโฏ
|
24 |
-
|
25 |
-
|
26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
st.stop()
|
28 |
|
29 |
-
|
30 |
-
genai.configure(api_key=API_KEY)
|
31 |
GEM_MODEL = "gemini-1.5-pro-latest"
|
32 |
TMP = Path(tempfile.gettempdir())
|
33 |
|
34 |
-
# โโโโโโโโโโโโโโโโโโโโโ 1โฏยทโฏ
|
35 |
@st.cache_data(show_spinner=False)
|
36 |
-
def
|
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
|
46 |
return create_engine(uri).table_names()
|
47 |
|
48 |
@st.cache_data(show_spinner=True)
|
49 |
-
def
|
50 |
return pd.read_sql_table(tbl, create_engine(uri))
|
51 |
|
52 |
@st.cache_data(show_spinner=False)
|
53 |
-
def
|
54 |
return genai.GenerativeModel(GEM_MODEL).generate_content(prompt).text.strip()
|
55 |
|
56 |
-
# โโโโโโโโโโโโโโโโโโโโโ 2โฏยทโฏDATA
|
57 |
-
st.title("๐
|
58 |
-
mode = st.sidebar.radio("
|
59 |
-
df: pd.DataFrame = pd.DataFrame()
|
60 |
|
61 |
-
|
62 |
-
|
63 |
-
|
|
|
64 |
if upl:
|
65 |
-
df =
|
66 |
else:
|
67 |
uri = st.sidebar.text_input("SQLAlchemy URI")
|
68 |
if uri:
|
69 |
-
tbl = st.sidebar.selectbox("
|
70 |
if tbl:
|
71 |
-
df =
|
72 |
|
73 |
if df.empty:
|
74 |
-
st.info("โฌ
๏ธ
|
75 |
st.stop()
|
76 |
|
77 |
-
st.success("โ
|
78 |
st.dataframe(df.head(), use_container_width=True)
|
79 |
|
80 |
-
# โโโโโโโโโโโโโโโโโโโโโ 3โฏยทโฏ
|
81 |
rows, cols = df.shape
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
st.subheader("๐ง
|
89 |
-
with st.spinner("Generating
|
90 |
-
|
91 |
-
st.markdown(
|
92 |
-
"You are a senior BI analyst.
|
93 |
))
|
94 |
|
95 |
-
# โโโโโโโโโโโโโโโโโโโโโ 4โฏยทโฏTIME
|
96 |
-
#
|
97 |
-
for
|
98 |
-
if not pd.api.types.is_datetime64_any_dtype(df[
|
99 |
try:
|
100 |
-
df[
|
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 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
st.
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
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 |
-
|
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",
|
136 |
|
137 |
-
# โโโโโโโโโโโโโโโโโโโโโ 7โฏยทโฏSTRATEGY
|
138 |
brief = (
|
139 |
"# Strategy Brief\n"
|
140 |
-
"
|
141 |
-
"
|
142 |
-
"
|
143 |
-
"
|
144 |
-
"
|
145 |
)
|
146 |
-
st.download_button("โฌ๏ธ
|
|
|
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")
|