Spaces:
Sleeping
Sleeping
Update streamlit_app.py
Browse files- streamlit_app.py +46 -16
streamlit_app.py
CHANGED
|
@@ -138,6 +138,13 @@ def normalize_summary_payload(summary: dict) -> dict:
|
|
| 138 |
return normalized
|
| 139 |
|
| 140 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
def apply_filters(df: pd.DataFrame) -> pd.DataFrame:
|
| 142 |
st.sidebar.header("Filters")
|
| 143 |
|
|
@@ -231,25 +238,52 @@ def render_bullet_list(items: list[str], empty_message: str) -> None:
|
|
| 231 |
st.markdown(f"- {item}")
|
| 232 |
|
| 233 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 234 |
def render_daily_summary_source_basis(
|
| 235 |
df: pd.DataFrame,
|
| 236 |
summary: dict,
|
| 237 |
) -> pd.DataFrame:
|
| 238 |
-
|
| 239 |
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
else:
|
| 243 |
-
summary_df = df
|
| 244 |
|
| 245 |
-
if
|
| 246 |
st.caption(
|
| 247 |
-
f"Summary is based on
|
| 248 |
-
f"
|
|
|
|
| 249 |
)
|
| 250 |
else:
|
| 251 |
st.caption(
|
| 252 |
-
|
|
|
|
| 253 |
)
|
| 254 |
|
| 255 |
return summary_df
|
|
@@ -345,7 +379,7 @@ def render_daily_summary(summary: dict) -> None:
|
|
| 345 |
st.markdown("**Decision relevance**")
|
| 346 |
st.write(decision_relevance)
|
| 347 |
|
| 348 |
-
if url:
|
| 349 |
st.link_button("Open article", url)
|
| 350 |
|
| 351 |
if article_id:
|
|
@@ -411,8 +445,8 @@ def render_article_browser(df: pd.DataFrame) -> None:
|
|
| 411 |
st.write(description)
|
| 412 |
|
| 413 |
url = row.get("url")
|
| 414 |
-
if
|
| 415 |
-
st.link_button("Open article",
|
| 416 |
|
| 417 |
st.markdown("**More details**")
|
| 418 |
|
|
@@ -432,10 +466,6 @@ def main() -> None:
|
|
| 432 |
"with filters for action categories, dates, sources, and search terms."
|
| 433 |
)
|
| 434 |
|
| 435 |
-
if st.sidebar.button("Refresh data"):
|
| 436 |
-
st.cache_data.clear()
|
| 437 |
-
st.rerun()
|
| 438 |
-
|
| 439 |
df = load_classified_articles()
|
| 440 |
summary = load_daily_summary()
|
| 441 |
|
|
|
|
| 138 |
return normalized
|
| 139 |
|
| 140 |
|
| 141 |
+
def is_valid_url(value: object) -> bool:
|
| 142 |
+
if not isinstance(value, str):
|
| 143 |
+
return False
|
| 144 |
+
|
| 145 |
+
return value.startswith(("http://", "https://"))
|
| 146 |
+
|
| 147 |
+
|
| 148 |
def apply_filters(df: pd.DataFrame) -> pd.DataFrame:
|
| 149 |
st.sidebar.header("Filters")
|
| 150 |
|
|
|
|
| 238 |
st.markdown(f"- {item}")
|
| 239 |
|
| 240 |
|
| 241 |
+
def get_summary_source_articles(
|
| 242 |
+
df: pd.DataFrame,
|
| 243 |
+
summary: dict,
|
| 244 |
+
fallback_limit: int = 15,
|
| 245 |
+
) -> pd.DataFrame:
|
| 246 |
+
stories = summary.get("top_stories", [])
|
| 247 |
+
|
| 248 |
+
story_ids = {
|
| 249 |
+
str(story.get("article_id"))
|
| 250 |
+
for story in stories
|
| 251 |
+
if isinstance(story, dict) and story.get("article_id")
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
if story_ids:
|
| 255 |
+
matched_df = df[df["article_id"].astype(str).isin(story_ids)]
|
| 256 |
+
|
| 257 |
+
if not matched_df.empty:
|
| 258 |
+
return matched_df
|
| 259 |
+
|
| 260 |
+
relevant_df = df[df["label"] != "not relevant to field"].copy()
|
| 261 |
+
|
| 262 |
+
if "published_at" in relevant_df:
|
| 263 |
+
relevant_df = relevant_df.sort_values("published_at", ascending=False)
|
| 264 |
+
|
| 265 |
+
return relevant_df.head(fallback_limit)
|
| 266 |
+
|
| 267 |
+
|
| 268 |
def render_daily_summary_source_basis(
|
| 269 |
df: pd.DataFrame,
|
| 270 |
summary: dict,
|
| 271 |
) -> pd.DataFrame:
|
| 272 |
+
summary_df = get_summary_source_articles(df, summary)
|
| 273 |
|
| 274 |
+
generated_at = summary.get("generated_at")
|
| 275 |
+
top_story_count = len(summary.get("top_stories", []))
|
|
|
|
|
|
|
| 276 |
|
| 277 |
+
if generated_at:
|
| 278 |
st.caption(
|
| 279 |
+
f"Summary is based on the latest relevant classified articles available "
|
| 280 |
+
f"when it was generated at {generated_at}. "
|
| 281 |
+
f"{top_story_count} top stories are shown in the summary."
|
| 282 |
)
|
| 283 |
else:
|
| 284 |
st.caption(
|
| 285 |
+
"Summary is based on the latest relevant classified articles available "
|
| 286 |
+
f"when it was generated. {top_story_count} top stories are shown in the summary."
|
| 287 |
)
|
| 288 |
|
| 289 |
return summary_df
|
|
|
|
| 379 |
st.markdown("**Decision relevance**")
|
| 380 |
st.write(decision_relevance)
|
| 381 |
|
| 382 |
+
if is_valid_url(url):
|
| 383 |
st.link_button("Open article", url)
|
| 384 |
|
| 385 |
if article_id:
|
|
|
|
| 445 |
st.write(description)
|
| 446 |
|
| 447 |
url = row.get("url")
|
| 448 |
+
if is_valid_url(url):
|
| 449 |
+
st.link_button("Open article", url)
|
| 450 |
|
| 451 |
st.markdown("**More details**")
|
| 452 |
|
|
|
|
| 466 |
"with filters for action categories, dates, sources, and search terms."
|
| 467 |
)
|
| 468 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 469 |
df = load_classified_articles()
|
| 470 |
summary = load_daily_summary()
|
| 471 |
|