Spaces:
Running
Running
Update streamlit_app.py
Browse files- streamlit_app.py +12 -19
streamlit_app.py
CHANGED
|
@@ -10,11 +10,10 @@ if Path("/data").exists():
|
|
| 10 |
else:
|
| 11 |
CSV_PATH = Path(__file__).resolve().parent / "policy_tracker.csv"
|
| 12 |
|
| 13 |
-
# --- DATA LOADING
|
| 14 |
def load_data():
|
| 15 |
if CSV_PATH.exists():
|
| 16 |
df = pd.read_csv(CSV_PATH, dtype=str)
|
| 17 |
-
# Convert and forcefully strip all timezones to prevent crashes
|
| 18 |
df['event_date'] = pd.to_datetime(df['event_date'], errors='coerce')
|
| 19 |
if df['event_date'].dt.tz is not None:
|
| 20 |
df['event_date'] = df['event_date'].dt.tz_localize(None)
|
|
@@ -24,15 +23,15 @@ def load_data():
|
|
| 24 |
return None
|
| 25 |
|
| 26 |
# --- UI SETUP ---
|
| 27 |
-
st.set_page_config(page_title="PolicyPilot Intelligence", layout="wide"
|
| 28 |
-
st.title("
|
| 29 |
|
| 30 |
df = load_data()
|
| 31 |
|
| 32 |
# --- SIDEBAR ---
|
| 33 |
with st.sidebar:
|
| 34 |
st.header("Controls")
|
| 35 |
-
if st.button("
|
| 36 |
with st.spinner("Syncing Gov Schedules, API, Agencies, and Media..."):
|
| 37 |
importlib.reload(main)
|
| 38 |
count = main.run()
|
|
@@ -43,7 +42,7 @@ with st.sidebar:
|
|
| 43 |
st.header("Triage Filters")
|
| 44 |
|
| 45 |
if df is not None and not df.empty:
|
| 46 |
-
show_high_only = st.checkbox("
|
| 47 |
available_types = df['type'].dropna().unique().tolist()
|
| 48 |
selected_types = st.multiselect(
|
| 49 |
"Filter by Source Type:",
|
|
@@ -53,14 +52,14 @@ with st.sidebar:
|
|
| 53 |
|
| 54 |
# --- EXECUTIVE BRIEFING ---
|
| 55 |
if df is not None and not df.empty:
|
| 56 |
-
st.subheader("
|
| 57 |
|
| 58 |
if 'executive_summary' not in st.session_state:
|
| 59 |
st.session_state.executive_summary = "Click the button below to generate a high-level briefing of recent updates."
|
| 60 |
|
| 61 |
st.info(st.session_state.executive_summary)
|
| 62 |
|
| 63 |
-
if st.button("
|
| 64 |
with st.spinner("AI is analyzing the latest updates..."):
|
| 65 |
top_items = df.head(10)
|
| 66 |
context_text = "\n".join([f"- {row['title']} (Source: {row['source']})" for _, row in top_items.iterrows()])
|
|
@@ -87,21 +86,18 @@ def render_event_cards(display_df, is_radar=False):
|
|
| 87 |
return
|
| 88 |
|
| 89 |
for _, row in display_df.iterrows():
|
| 90 |
-
# Setup the date strings
|
| 91 |
event_dt = row['event_date']
|
| 92 |
event_str = event_dt.strftime('%b %d, %Y') if pd.notnull(event_dt) else "Date Unknown"
|
| 93 |
|
| 94 |
-
# Countdown logic for the Radar tab
|
| 95 |
countdown_badge = ""
|
| 96 |
if is_radar and pd.notnull(event_dt):
|
| 97 |
-
# Timezone naive now()
|
| 98 |
days_until = (event_dt.date() - pd.Timestamp.now().tz_localize(None).date()).days
|
| 99 |
if days_until == 0:
|
| 100 |
-
countdown_badge = " [
|
| 101 |
elif days_until > 0:
|
| 102 |
-
countdown_badge = f" [
|
| 103 |
|
| 104 |
-
flag = row.get('triage_flag', '
|
| 105 |
|
| 106 |
with st.expander(f"{flag}{countdown_badge} | {event_str} | {row['type']}: {row['source']} | {row['title'][:65]}..."):
|
| 107 |
col1, col2 = st.columns([3, 1])
|
|
@@ -117,18 +113,16 @@ def render_event_cards(display_df, is_radar=False):
|
|
| 117 |
st.link_button("Review Original Source", str(row['link']), use_container_width=True)
|
| 118 |
|
| 119 |
if df is not None and not df.empty:
|
| 120 |
-
# 1. Apply Filters
|
| 121 |
filtered_df = df.copy()
|
| 122 |
if selected_types:
|
| 123 |
filtered_df = filtered_df[filtered_df['type'].isin(selected_types)]
|
| 124 |
if show_high_only:
|
| 125 |
filtered_df = filtered_df[filtered_df['triage_flag'].str.contains("HIGH", na=False)]
|
| 126 |
|
| 127 |
-
search = st.text_input("
|
| 128 |
if search:
|
| 129 |
filtered_df = filtered_df[filtered_df.apply(lambda r: r.astype(str).str.contains(search, case=False).any(), axis=1)]
|
| 130 |
|
| 131 |
-
# 2. Timezone-naive date splitting
|
| 132 |
today = pd.Timestamp.now().tz_localize(None).normalize()
|
| 133 |
|
| 134 |
is_future = filtered_df['event_date'] >= today
|
|
@@ -137,8 +131,7 @@ if df is not None and not df.empty:
|
|
| 137 |
radar_df = filtered_df[is_future].sort_values(by="event_date", ascending=True)
|
| 138 |
archive_df = filtered_df[is_past].sort_values(by="event_date", ascending=False)
|
| 139 |
|
| 140 |
-
|
| 141 |
-
tab1, tab2 = st.tabs([f"π‘ Radar (Upcoming: {len(radar_df)})", f"ποΈ Archive (Past: {len(archive_df)})"])
|
| 142 |
|
| 143 |
with tab1:
|
| 144 |
st.subheader("Upcoming Events & Scheduled Action")
|
|
|
|
| 10 |
else:
|
| 11 |
CSV_PATH = Path(__file__).resolve().parent / "policy_tracker.csv"
|
| 12 |
|
| 13 |
+
# --- DATA LOADING ---
|
| 14 |
def load_data():
|
| 15 |
if CSV_PATH.exists():
|
| 16 |
df = pd.read_csv(CSV_PATH, dtype=str)
|
|
|
|
| 17 |
df['event_date'] = pd.to_datetime(df['event_date'], errors='coerce')
|
| 18 |
if df['event_date'].dt.tz is not None:
|
| 19 |
df['event_date'] = df['event_date'].dt.tz_localize(None)
|
|
|
|
| 23 |
return None
|
| 24 |
|
| 25 |
# --- UI SETUP ---
|
| 26 |
+
st.set_page_config(page_title="PolicyPilot Intelligence", layout="wide")
|
| 27 |
+
st.title("PolicyPilot Intelligence Dashboard")
|
| 28 |
|
| 29 |
df = load_data()
|
| 30 |
|
| 31 |
# --- SIDEBAR ---
|
| 32 |
with st.sidebar:
|
| 33 |
st.header("Controls")
|
| 34 |
+
if st.button("Run Live Sweep", use_container_width=True):
|
| 35 |
with st.spinner("Syncing Gov Schedules, API, Agencies, and Media..."):
|
| 36 |
importlib.reload(main)
|
| 37 |
count = main.run()
|
|
|
|
| 42 |
st.header("Triage Filters")
|
| 43 |
|
| 44 |
if df is not None and not df.empty:
|
| 45 |
+
show_high_only = st.checkbox("Show HIGH PRIORITY Only", value=False)
|
| 46 |
available_types = df['type'].dropna().unique().tolist()
|
| 47 |
selected_types = st.multiselect(
|
| 48 |
"Filter by Source Type:",
|
|
|
|
| 52 |
|
| 53 |
# --- EXECUTIVE BRIEFING ---
|
| 54 |
if df is not None and not df.empty:
|
| 55 |
+
st.subheader("Latest Intel Summary")
|
| 56 |
|
| 57 |
if 'executive_summary' not in st.session_state:
|
| 58 |
st.session_state.executive_summary = "Click the button below to generate a high-level briefing of recent updates."
|
| 59 |
|
| 60 |
st.info(st.session_state.executive_summary)
|
| 61 |
|
| 62 |
+
if st.button("Generate Executive Briefing"):
|
| 63 |
with st.spinner("AI is analyzing the latest updates..."):
|
| 64 |
top_items = df.head(10)
|
| 65 |
context_text = "\n".join([f"- {row['title']} (Source: {row['source']})" for _, row in top_items.iterrows()])
|
|
|
|
| 86 |
return
|
| 87 |
|
| 88 |
for _, row in display_df.iterrows():
|
|
|
|
| 89 |
event_dt = row['event_date']
|
| 90 |
event_str = event_dt.strftime('%b %d, %Y') if pd.notnull(event_dt) else "Date Unknown"
|
| 91 |
|
|
|
|
| 92 |
countdown_badge = ""
|
| 93 |
if is_radar and pd.notnull(event_dt):
|
|
|
|
| 94 |
days_until = (event_dt.date() - pd.Timestamp.now().tz_localize(None).date()).days
|
| 95 |
if days_until == 0:
|
| 96 |
+
countdown_badge = " [TODAY]"
|
| 97 |
elif days_until > 0:
|
| 98 |
+
countdown_badge = f" [In {days_until} days]"
|
| 99 |
|
| 100 |
+
flag = row.get('triage_flag', 'LOW - MONITOR')
|
| 101 |
|
| 102 |
with st.expander(f"{flag}{countdown_badge} | {event_str} | {row['type']}: {row['source']} | {row['title'][:65]}..."):
|
| 103 |
col1, col2 = st.columns([3, 1])
|
|
|
|
| 113 |
st.link_button("Review Original Source", str(row['link']), use_container_width=True)
|
| 114 |
|
| 115 |
if df is not None and not df.empty:
|
|
|
|
| 116 |
filtered_df = df.copy()
|
| 117 |
if selected_types:
|
| 118 |
filtered_df = filtered_df[filtered_df['type'].isin(selected_types)]
|
| 119 |
if show_high_only:
|
| 120 |
filtered_df = filtered_df[filtered_df['triage_flag'].str.contains("HIGH", na=False)]
|
| 121 |
|
| 122 |
+
search = st.text_input("Search matches...", "")
|
| 123 |
if search:
|
| 124 |
filtered_df = filtered_df[filtered_df.apply(lambda r: r.astype(str).str.contains(search, case=False).any(), axis=1)]
|
| 125 |
|
|
|
|
| 126 |
today = pd.Timestamp.now().tz_localize(None).normalize()
|
| 127 |
|
| 128 |
is_future = filtered_df['event_date'] >= today
|
|
|
|
| 131 |
radar_df = filtered_df[is_future].sort_values(by="event_date", ascending=True)
|
| 132 |
archive_df = filtered_df[is_past].sort_values(by="event_date", ascending=False)
|
| 133 |
|
| 134 |
+
tab1, tab2 = st.tabs([f"Radar (Upcoming: {len(radar_df)})", f"Archive (Past: {len(archive_df)})"])
|
|
|
|
| 135 |
|
| 136 |
with tab1:
|
| 137 |
st.subheader("Upcoming Events & Scheduled Action")
|