Spaces:
Sleeping
Sleeping
Update src/app.py
Browse files- src/app.py +39 -61
src/app.py
CHANGED
|
@@ -33,10 +33,17 @@ if str(ROOT) not in sys.path:
|
|
| 33 |
for d in ["data/uploads", "data/faiss_index"]:
|
| 34 |
Path(d).mkdir(parents=True, exist_ok=True)
|
| 35 |
|
| 36 |
-
#
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
from core.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
|
| 41 |
logging.basicConfig(level=logging.INFO)
|
| 42 |
logger = logging.getLogger(__name__)
|
|
@@ -56,7 +63,7 @@ st.set_page_config(
|
|
| 56 |
st.markdown("""
|
| 57 |
<style>
|
| 58 |
/* ββ Import Fonts ββ */
|
| 59 |
-
|
| 60 |
|
| 61 |
/* ββ Root Variables ββ */
|
| 62 |
:root {
|
|
@@ -299,14 +306,16 @@ with st.sidebar:
|
|
| 299 |
# Consultant factory (cached per token)
|
| 300 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 301 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 302 |
def _get_consultant() -> ESGConsultant:
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
token = st.session_state.hf_token or "NO_TOKEN"
|
| 308 |
-
st.session_state.consultant = ESGConsultant(hf_token=token)
|
| 309 |
-
return st.session_state.consultant
|
| 310 |
|
| 311 |
|
| 312 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -520,13 +529,10 @@ def page_dashboard():
|
|
| 520 |
line=dict(color="#2ECC71", width=2), marker=dict(size=6)))
|
| 521 |
_hline(fig_el)
|
| 522 |
fig_el.update_layout(**LAYOUT, height=260,
|
| 523 |
-
title=dict(text="Renewable Energy % Over Time", font=dict(color="#D4AF37"))
|
| 524 |
-
|
| 525 |
-
|
| 526 |
-
|
| 527 |
-
title="Renewable %",
|
| 528 |
-
range=[0,115]
|
| 529 |
-
)
|
| 530 |
with st.expander("π Energy Data Table"):
|
| 531 |
st.dataframe(edf, width="stretch", hide_index=True)
|
| 532 |
|
|
@@ -566,11 +572,8 @@ def page_dashboard():
|
|
| 566 |
fig_w.add_trace(go.Bar(x=periods, y=wdf["rainwater_kl"],
|
| 567 |
name="Rainwater Harvesting", marker_color="#2ECC71"))
|
| 568 |
fig_w.update_layout(**LAYOUT, barmode="stack", height=320,
|
| 569 |
-
title=dict(text="Water by Source (Kilolitres)", font=dict(color="#D4AF37"))
|
| 570 |
-
|
| 571 |
-
fig_el.update_yaxes(
|
| 572 |
-
gridcolor="rgba(255,255,255,0.06)",
|
| 573 |
-
title="kL")
|
| 574 |
st.plotly_chart(fig_w, width="stretch")
|
| 575 |
|
| 576 |
with wc2:
|
|
@@ -623,11 +626,8 @@ def page_dashboard():
|
|
| 623 |
fig_wst.add_trace(go.Bar(x=periods, y=wst["disposed_kg"],
|
| 624 |
name="Disposed (kg)", marker_color="#E74C3C", opacity=0.8))
|
| 625 |
fig_wst.update_layout(**LAYOUT, barmode="group", height=320,
|
| 626 |
-
title=dict(text="Waste Recovered vs Disposed (kg)", font=dict(color="#D4AF37"))
|
| 627 |
-
|
| 628 |
-
fig_el.update_yaxes(
|
| 629 |
-
gridcolor="rgba(255,255,255,0.06)",
|
| 630 |
-
title="kg")
|
| 631 |
st.plotly_chart(fig_wst, width="stretch")
|
| 632 |
|
| 633 |
with wst2:
|
|
@@ -646,13 +646,8 @@ def page_dashboard():
|
|
| 646 |
fig_rp.add_hline(y=50, line_dash="dot", line_color="#D4AF37",
|
| 647 |
annotation_text="50% Target", annotation_font=dict(color="#D4AF37"))
|
| 648 |
fig_rp.update_layout(**LAYOUT, height=320,
|
| 649 |
-
title=dict(text="Waste Recovery Rate (%)", font=dict(color="#D4AF37"))
|
| 650 |
-
|
| 651 |
-
fig_rp.update_yaxes(
|
| 652 |
-
gridcolor="rgba(255,255,255,0.06)",
|
| 653 |
-
title="%",
|
| 654 |
-
range=[0, 110]
|
| 655 |
-
)
|
| 656 |
st.plotly_chart(fig_rp, width="stretch")
|
| 657 |
|
| 658 |
# KPI cards
|
|
@@ -677,12 +672,8 @@ def page_dashboard():
|
|
| 677 |
if col in wdf.columns:
|
| 678 |
fig.add_trace(go.Bar(x=wdf["period"], y=wdf[col],
|
| 679 |
name=name, marker_color=color, opacity=0.85))
|
| 680 |
-
fig.update_layout(**LAYOUT, barmode="group", height=300
|
| 681 |
-
|
| 682 |
-
fig.update_yaxes(
|
| 683 |
-
gridcolor="rgba(255,255,255,0.06)",
|
| 684 |
-
title="kg"
|
| 685 |
-
)
|
| 686 |
st.plotly_chart(fig, width="stretch")
|
| 687 |
else:
|
| 688 |
st.info("No waste data detected. Upload your SPJIMR Environmental Metrics XLSX.")
|
|
@@ -774,13 +765,8 @@ def page_dashboard():
|
|
| 774 |
_add_forecast_traces(fig_ef, list(edf_f["period"]), edf_f["renewable_pct"].values, fl, "#2ECC71", "Renewable %")
|
| 775 |
_hline(fig_ef)
|
| 776 |
fig_ef.update_layout(**LAYOUT, height=320,
|
| 777 |
-
title=dict(text=f"Renewable Energy % Forecast (next {horizon} months)", font=dict(color="#D4AF37"))
|
| 778 |
-
|
| 779 |
-
fig_ef.update_yaxes(
|
| 780 |
-
gridcolor="rgba(255,255,255,0.06)",
|
| 781 |
-
title="%",
|
| 782 |
-
range=[0,115]
|
| 783 |
-
)
|
| 784 |
st.plotly_chart(fig_ef, width="stretch")
|
| 785 |
|
| 786 |
# Waste forecast
|
|
@@ -795,12 +781,8 @@ def page_dashboard():
|
|
| 795 |
line=dict(color=color, width=2), marker=dict(size=5)))
|
| 796 |
_add_forecast_traces(fig_wf, list(wst_f["period"]), wst_f[col].values, fl, color, name)
|
| 797 |
fig_wf.update_layout(**LAYOUT, height=320,
|
| 798 |
-
title=dict(text=f"Waste Forecast (next {horizon} months)", font=dict(color="#D4AF37"))
|
| 799 |
-
|
| 800 |
-
fig_wf.update_yaxes(
|
| 801 |
-
gridcolor="rgba(255,255,255,0.06)",
|
| 802 |
-
title="kg"
|
| 803 |
-
)
|
| 804 |
st.plotly_chart(fig_wf, width="stretch")
|
| 805 |
|
| 806 |
# Water forecast
|
|
@@ -814,12 +796,8 @@ def page_dashboard():
|
|
| 814 |
line=dict(color="#3498DB", width=2), marker=dict(size=5)))
|
| 815 |
_add_forecast_traces(fig_wtrf, list(wtr_f["period"]), wtr_f["total_kl"].values, fl, "#3498DB", "Total Water")
|
| 816 |
fig_wtrf.update_layout(**LAYOUT, height=300,
|
| 817 |
-
title=dict(text=f"Water Consumption Forecast (next {horizon} months)", font=dict(color="#D4AF37"))
|
| 818 |
-
|
| 819 |
-
fig_wtrf.update_yaxes(
|
| 820 |
-
gridcolor="rgba(255,255,255,0.06)",
|
| 821 |
-
title="kL"
|
| 822 |
-
)
|
| 823 |
st.plotly_chart(fig_wtrf, width="stretch")
|
| 824 |
|
| 825 |
if not has_fc:
|
|
|
|
| 33 |
for d in ["data/uploads", "data/faiss_index"]:
|
| 34 |
Path(d).mkdir(parents=True, exist_ok=True)
|
| 35 |
|
| 36 |
+
# Heavy deps imported lazily inside @st.cache_resource to avoid blocking startup
|
| 37 |
+
@st.cache_resource(show_spinner="β³ Loading ESG engineβ¦")
|
| 38 |
+
def _load_modules():
|
| 39 |
+
from core.processor import (DocumentProcessor, extract_waste_series,
|
| 40 |
+
extract_energy_series, extract_spjimr_metrics_raw)
|
| 41 |
+
from core.consultant import ESGConsultant
|
| 42 |
+
return DocumentProcessor, extract_waste_series, extract_energy_series, extract_spjimr_metrics_raw, ESGConsultant
|
| 43 |
+
|
| 44 |
+
# Unpack β this blocks only on first run, cached on all subsequent reruns
|
| 45 |
+
(DocumentProcessor, extract_waste_series,
|
| 46 |
+
extract_energy_series, extract_spjimr_metrics_raw, ESGConsultant) = _load_modules()
|
| 47 |
|
| 48 |
logging.basicConfig(level=logging.INFO)
|
| 49 |
logger = logging.getLogger(__name__)
|
|
|
|
| 63 |
st.markdown("""
|
| 64 |
<style>
|
| 65 |
/* ββ Import Fonts ββ */
|
| 66 |
+
/* Fonts: system stack β avoids network round-trip on cold start */
|
| 67 |
|
| 68 |
/* ββ Root Variables ββ */
|
| 69 |
:root {
|
|
|
|
| 306 |
# Consultant factory (cached per token)
|
| 307 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 308 |
|
| 309 |
+
@st.cache_resource(show_spinner=False)
|
| 310 |
+
def _cached_consultant(token: str) -> ESGConsultant:
|
| 311 |
+
"""One instance per unique token β cached across all reruns."""
|
| 312 |
+
return ESGConsultant(hf_token=token or "NO_TOKEN")
|
| 313 |
+
|
| 314 |
def _get_consultant() -> ESGConsultant:
|
| 315 |
+
token = st.session_state.hf_token or ""
|
| 316 |
+
c = _cached_consultant(token)
|
| 317 |
+
st.session_state.consultant = c
|
| 318 |
+
return c
|
|
|
|
|
|
|
|
|
|
| 319 |
|
| 320 |
|
| 321 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 529 |
line=dict(color="#2ECC71", width=2), marker=dict(size=6)))
|
| 530 |
_hline(fig_el)
|
| 531 |
fig_el.update_layout(**LAYOUT, height=260,
|
| 532 |
+
title=dict(text="Renewable Energy % Over Time", font=dict(color="#D4AF37")),
|
| 533 |
+
yaxis=dict(gridcolor="rgba(255,255,255,0.06)", title="Renewable %", range=[0, 115]))
|
| 534 |
+
st.plotly_chart(fig_el, width="stretch")
|
| 535 |
+
|
|
|
|
|
|
|
|
|
|
| 536 |
with st.expander("π Energy Data Table"):
|
| 537 |
st.dataframe(edf, width="stretch", hide_index=True)
|
| 538 |
|
|
|
|
| 572 |
fig_w.add_trace(go.Bar(x=periods, y=wdf["rainwater_kl"],
|
| 573 |
name="Rainwater Harvesting", marker_color="#2ECC71"))
|
| 574 |
fig_w.update_layout(**LAYOUT, barmode="stack", height=320,
|
| 575 |
+
title=dict(text="Water by Source (Kilolitres)", font=dict(color="#D4AF37")),
|
| 576 |
+
yaxis=dict(gridcolor="rgba(255,255,255,0.06)", title="kL"))
|
|
|
|
|
|
|
|
|
|
| 577 |
st.plotly_chart(fig_w, width="stretch")
|
| 578 |
|
| 579 |
with wc2:
|
|
|
|
| 626 |
fig_wst.add_trace(go.Bar(x=periods, y=wst["disposed_kg"],
|
| 627 |
name="Disposed (kg)", marker_color="#E74C3C", opacity=0.8))
|
| 628 |
fig_wst.update_layout(**LAYOUT, barmode="group", height=320,
|
| 629 |
+
title=dict(text="Waste Recovered vs Disposed (kg)", font=dict(color="#D4AF37")),
|
| 630 |
+
yaxis=dict(gridcolor="rgba(255,255,255,0.06)", title="kg"))
|
|
|
|
|
|
|
|
|
|
| 631 |
st.plotly_chart(fig_wst, width="stretch")
|
| 632 |
|
| 633 |
with wst2:
|
|
|
|
| 646 |
fig_rp.add_hline(y=50, line_dash="dot", line_color="#D4AF37",
|
| 647 |
annotation_text="50% Target", annotation_font=dict(color="#D4AF37"))
|
| 648 |
fig_rp.update_layout(**LAYOUT, height=320,
|
| 649 |
+
title=dict(text="Waste Recovery Rate (%)", font=dict(color="#D4AF37")),
|
| 650 |
+
yaxis=dict(gridcolor="rgba(255,255,255,0.06)", title="%", range=[0, 110]))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 651 |
st.plotly_chart(fig_rp, width="stretch")
|
| 652 |
|
| 653 |
# KPI cards
|
|
|
|
| 672 |
if col in wdf.columns:
|
| 673 |
fig.add_trace(go.Bar(x=wdf["period"], y=wdf[col],
|
| 674 |
name=name, marker_color=color, opacity=0.85))
|
| 675 |
+
fig.update_layout(**LAYOUT, barmode="group", height=300,
|
| 676 |
+
yaxis=dict(gridcolor="rgba(255,255,255,0.06)", title="kg"))
|
|
|
|
|
|
|
|
|
|
|
|
|
| 677 |
st.plotly_chart(fig, width="stretch")
|
| 678 |
else:
|
| 679 |
st.info("No waste data detected. Upload your SPJIMR Environmental Metrics XLSX.")
|
|
|
|
| 765 |
_add_forecast_traces(fig_ef, list(edf_f["period"]), edf_f["renewable_pct"].values, fl, "#2ECC71", "Renewable %")
|
| 766 |
_hline(fig_ef)
|
| 767 |
fig_ef.update_layout(**LAYOUT, height=320,
|
| 768 |
+
title=dict(text=f"Renewable Energy % Forecast (next {horizon} months)", font=dict(color="#D4AF37")),
|
| 769 |
+
yaxis=dict(gridcolor="rgba(255,255,255,0.06)", title="%", range=[0,115]))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 770 |
st.plotly_chart(fig_ef, width="stretch")
|
| 771 |
|
| 772 |
# Waste forecast
|
|
|
|
| 781 |
line=dict(color=color, width=2), marker=dict(size=5)))
|
| 782 |
_add_forecast_traces(fig_wf, list(wst_f["period"]), wst_f[col].values, fl, color, name)
|
| 783 |
fig_wf.update_layout(**LAYOUT, height=320,
|
| 784 |
+
title=dict(text=f"Waste Forecast (next {horizon} months)", font=dict(color="#D4AF37")),
|
| 785 |
+
yaxis=dict(gridcolor="rgba(255,255,255,0.06)", title="kg"))
|
|
|
|
|
|
|
|
|
|
|
|
|
| 786 |
st.plotly_chart(fig_wf, width="stretch")
|
| 787 |
|
| 788 |
# Water forecast
|
|
|
|
| 796 |
line=dict(color="#3498DB", width=2), marker=dict(size=5)))
|
| 797 |
_add_forecast_traces(fig_wtrf, list(wtr_f["period"]), wtr_f["total_kl"].values, fl, "#3498DB", "Total Water")
|
| 798 |
fig_wtrf.update_layout(**LAYOUT, height=300,
|
| 799 |
+
title=dict(text=f"Water Consumption Forecast (next {horizon} months)", font=dict(color="#D4AF37")),
|
| 800 |
+
yaxis=dict(gridcolor="rgba(255,255,255,0.06)", title="kL"))
|
|
|
|
|
|
|
|
|
|
|
|
|
| 801 |
st.plotly_chart(fig_wtrf, width="stretch")
|
| 802 |
|
| 803 |
if not has_fc:
|