Skip to content

Commit 09b95fb

Browse files
committed
feat: vertical layout, navy sidebar, soft blue theme, clearer gridlines
1 parent b7b3ed5 commit 09b95fb

4 files changed

Lines changed: 375 additions & 164 deletions

File tree

.streamlit/config.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[theme]
2+
primaryColor = "#1B4F8A"
3+
backgroundColor = "#F0F4F9"
4+
secondaryBackgroundColor = "#E4EBF4"
5+
textColor = "#1A1A1A"
6+
font = "serif"

Homepage.py

Lines changed: 113 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import time
22

3-
import matplotlib.pyplot as plt
43
import pandas as pd
4+
import plotly.express as px
5+
import plotly.graph_objects as go
56
import streamlit as st
67
from google.cloud import bigquery
78
from google.oauth2 import service_account
@@ -13,25 +14,50 @@
1314
st.set_page_config(page_title="Weekly U.S. Petroleum Supply", layout="wide")
1415

1516
# =========================
16-
# Sidebar title
17+
# Sidebar title (above nav via CSS)
1718
# =========================
18-
st.sidebar.markdown(
19+
st.markdown(
1920
"""
20-
<h1 style="font-size: 1.5rem; line-height: 1.2; margin-bottom: 0.2rem;">
21-
U.S. Petroleum & WTI Weekly Monitor
22-
</h1>
21+
<style>
22+
[data-testid="stSidebar"] {
23+
background-color: #0D2B5E !important;
24+
}
25+
[data-testid="stSidebar"] * {
26+
color: white !important;
27+
}
28+
[data-testid="stSidebarNav"] a {
29+
color: rgba(255,255,255,0.8) !important;
30+
}
31+
[data-testid="stSidebarNav"] a:hover {
32+
color: white !important;
33+
background-color: rgba(255,255,255,0.1) !important;
34+
}
35+
[data-testid="stSidebarNav"] {
36+
padding-top: 3.5rem;
37+
}
38+
[data-testid="stSidebarNav"]::before {
39+
content: "U.S. Petroleum & WTI Weekly Monitor";
40+
display: block;
41+
position: absolute;
42+
top: 0;
43+
left: 0;
44+
right: 0;
45+
padding: 1rem 1.2rem 0.2rem 1.2rem;
46+
font-size: 1.05rem;
47+
font-weight: 600;
48+
line-height: 1.3;
49+
color: white !important;
50+
}
51+
</style>
2352
""",
2453
unsafe_allow_html=True,
2554
)
26-
st.sidebar.caption("Source: EIA")
27-
st.sidebar.divider()
2855

2956
# =========================
3057
# Main page header
3158
# =========================
3259
st.title("Weekly U.S. Petroleum Supply")
33-
st.subheader("Team Members: Irina, Indra")
34-
st.caption("Source: U.S. Energy Information Administration (EIA)")
60+
st.caption("Team Members: Irina, Indra · Source: U.S. Energy Information Administration (EIA)")
3561

3662
# =========================
3763
# Project Proposal
@@ -332,50 +358,71 @@ def compute_product_price_sensitivity(
332358
st.divider()
333359

334360
# =========================
335-
# Two side-by-side charts
361+
# Stacked charts
336362
# =========================
337-
left_col, right_col = st.columns(TWO_COLUMN_LAYOUT)
338-
339-
with left_col:
340-
st.subheader("Total Product Supplied")
341-
342-
fig, ax = plt.subplots(figsize=(7, 4))
343-
ax.plot(filtered_total["week"], filtered_total["total_supply"])
344-
ax.set_xlabel("Week")
345-
ax.set_ylabel("Total Product Supplied")
346-
st.pyplot(fig)
347-
348-
with st.expander("Show total supply data table"):
349-
total_display = filtered_total.sort_values("week", ascending=False).copy()
350-
total_display["week"] = total_display["week"].dt.strftime("%Y-%m-%d")
351-
st.dataframe(total_display, width="stretch")
352-
353-
with right_col:
354-
st.subheader("Product-Level Weekly Supply")
355-
356-
if not selected_products:
357-
st.warning("Please select at least one product from the sidebar.")
358-
else:
359-
product_plot_df = filtered_product[
360-
filtered_product["product_name"].isin(selected_products)
361-
].copy()
362-
363-
fig2, ax2 = plt.subplots(figsize=(7, 4))
364-
for product_name in selected_products:
365-
temp = product_plot_df[product_plot_df["product_name"] == product_name]
366-
ax2.plot(temp["week"], temp["product_supplied"], label=product_name)
367-
368-
ax2.set_xlabel("Week")
369-
ax2.set_ylabel("Product Supplied")
370-
ax2.legend()
371-
st.pyplot(fig2)
372-
373-
with st.expander("Show product-level data table"):
374-
product_display = product_plot_df.sort_values(
375-
["product_name", "week"], ascending=[True, False]
376-
).copy()
377-
product_display["week"] = product_display["week"].dt.strftime("%Y-%m-%d")
378-
st.dataframe(product_display, width="stretch")
363+
st.subheader("Total Product Supplied")
364+
365+
fig = px.line(
366+
filtered_total,
367+
x="week",
368+
y="total_supply",
369+
labels={"week": "Week", "total_supply": "Total Product Supplied"},
370+
)
371+
fig.update_layout(
372+
hovermode="x unified",
373+
xaxis=dict(gridcolor="rgba(13,43,94,0.2)", linecolor="#0D2B5E"),
374+
yaxis=dict(gridcolor="rgba(13,43,94,0.2)", linecolor="#0D2B5E"),
375+
plot_bgcolor="rgba(0,0,0,0)",
376+
paper_bgcolor="rgba(0,0,0,0)",
377+
)
378+
fig.update_traces(hovertemplate="<b>%{x|%b %d, %Y}</b><br>Total Supply: %{y:,.0f}<extra></extra>")
379+
st.plotly_chart(fig, use_container_width=True)
380+
381+
with st.expander("Show total supply data table"):
382+
total_display = filtered_total.sort_values("week", ascending=False).copy()
383+
total_display["week"] = total_display["week"].dt.strftime("%Y-%m-%d")
384+
st.dataframe(total_display, width="stretch")
385+
386+
st.divider()
387+
388+
st.subheader("Product-Level Weekly Supply")
389+
390+
if not selected_products:
391+
st.warning("Please select at least one product from the sidebar.")
392+
else:
393+
product_plot_df = filtered_product[
394+
filtered_product["product_name"].isin(selected_products)
395+
].copy()
396+
397+
fig2 = px.line(
398+
product_plot_df,
399+
x="week",
400+
y="product_supplied",
401+
color="product_name",
402+
labels={
403+
"week": "Week",
404+
"product_supplied": "Product Supplied",
405+
"product_name": "Product",
406+
},
407+
)
408+
fig2.update_layout(
409+
hovermode="x unified",
410+
xaxis=dict(gridcolor="rgba(13,43,94,0.2)", linecolor="#0D2B5E"),
411+
yaxis=dict(gridcolor="rgba(13,43,94,0.2)", linecolor="#0D2B5E"),
412+
plot_bgcolor="rgba(0,0,0,0)",
413+
paper_bgcolor="rgba(0,0,0,0)",
414+
)
415+
fig2.update_traces(
416+
hovertemplate="<b>%{fullData.name}</b><br>%{x|%b %d, %Y}: %{y:,.0f}<extra></extra>"
417+
)
418+
st.plotly_chart(fig2, use_container_width=True)
419+
420+
with st.expander("Show product-level data table"):
421+
product_display = product_plot_df.sort_values(
422+
["product_name", "week"], ascending=[True, False]
423+
).copy()
424+
product_display["week"] = product_display["week"].dt.strftime("%Y-%m-%d")
425+
st.dataframe(product_display, width="stretch")
379426

380427
st.divider()
381428

@@ -409,15 +456,20 @@ def compute_product_price_sensitivity(
409456
"abs_correlation", ascending=True
410457
)
411458

412-
fig3, ax3 = plt.subplots(figsize=(6, 3.5))
413-
ax3.barh(
414-
chart_df["product_name"],
415-
chart_df["abs_correlation"],
416-
color="darkorange",
459+
fig3 = px.bar(
460+
chart_df,
461+
x="abs_correlation",
462+
y="product_name",
463+
orientation="h",
464+
labels={
465+
"abs_correlation": "Absolute correlation with WTI price",
466+
"product_name": "Product",
467+
},
468+
color_discrete_sequence=["darkorange"],
417469
)
418-
ax3.set_xlabel("Absolute correlation with WTI price")
419-
ax3.set_ylabel("Product")
420-
st.pyplot(fig3)
470+
fig3.update_layout(showlegend=False)
471+
fig3.update_traces(hovertemplate="<b>%{y}</b><br>Correlation: %{x:.4f}<extra></extra>")
472+
st.plotly_chart(fig3, use_container_width=True)
421473

422474
with st.expander("Show product sensitivity table"):
423475
st.dataframe(

0 commit comments

Comments
 (0)