NKessler commited on
Commit
407afee
·
verified ·
1 Parent(s): 41068cb

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +96 -34
src/streamlit_app.py CHANGED
@@ -1,40 +1,102 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
  import streamlit as st
 
 
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
 
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
 
13
- In the meantime, below is an example of what you can do with just a few lines of code:
 
14
  """
15
 
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import typing
 
 
2
  import streamlit as st
3
+ from transformers import pipeline
4
+ import yake
5
+
6
+ MAX_TEXT_LENGTH = 1500
7
+ CANDIDATE_TONES = ["alarmist", "objective", "defensive", "optimistic", "critical"]
8
 
 
 
9
 
10
+ ARTICLE_A = """
11
+ Global leaders achieved a historic breakthrough today, signing a comprehensive climate accord aimed at drastically slashing carbon emissions by 2030. Environmental advocates are celebrating the mandate, which forces heavy-polluting industries to finally take accountability for their ecological damage. While corporations warn of transition costs, scientists emphasize that failing to act now would result in catastrophic, irreversible damage to our planet's fragile ecosystems.
12
+ """
13
 
14
+ ARTICLE_B = """
15
+ A sweeping new climate agreement signed today is drawing fierce criticism from industry leaders, who warn the aggressive emission targets will cripple economic growth. The heavy-handed regulations impose massive compliance costs on the manufacturing and energy sectors, inevitably leading to significant job losses and higher prices for consumers. Critics argue the rushed accord prioritizes bureaucratic posturing over practical, market-driven solutions to environmental concerns.
16
  """
17
 
18
+
19
+ @st.cache_resource
20
+ def _load_nlp_models() -> typing.Dict[str, typing.Any]:
21
+ sentiment_analyzer = pipeline(
22
+ "sentiment-analysis",
23
+ model="distilbert-base-uncased-finetuned-sst-2-english",
24
+ )
25
+ tone_classifier = pipeline(
26
+ "zero-shot-classification",
27
+ model="typeform/distilbert-base-uncased-mnli",
28
+ )
29
+ keyword_extractor = yake.KeywordExtractor(
30
+ lan="en", n=1, dedupLim=0.9, top=5, features=None
31
+ )
32
+
33
+ return {
34
+ "sentiment": sentiment_analyzer,
35
+ "tone": tone_classifier,
36
+ "keyword": keyword_extractor,
37
+ }
38
+
39
+
40
+ def analyze_article(text: str) -> dict:
41
+ models = _load_nlp_models()
42
+
43
+ safe_text = text[:MAX_TEXT_LENGTH]
44
+
45
+ sentiment_result = models["sentiment"](safe_text)[0]
46
+ tone_result = models["tone"](safe_text, CANDIDATE_TONES)
47
+ keyword_results = models["keyword"].extract_keywords(safe_text)
48
+
49
+ is_positive = sentiment_result["label"] == "POSITIVE"
50
+ sentiment_score = sentiment_result["score"] if is_positive else -sentiment_result["score"]
51
+
52
+ extracted_keywords = [kw[0] for kw in keyword_results]
53
+
54
+ return {
55
+ "sentiment_score": round(sentiment_score, 2),
56
+ "primary_tone": tone_result["labels"][0],
57
+ "keywords": extracted_keywords,
58
+ }
59
+
60
+
61
+ st.set_page_config(page_title="FrameVis MVP", layout="wide")
62
+ st.title("FrameVis: Media Framing Analyzer")
63
+ st.markdown("Compare how different news sources frame the same event using free, open-source NLP models.")
64
+
65
+ with st.spinner("Waking up NLP models (this takes a moment on startup)..."):
66
+ _load_nlp_models()
67
+
68
+ st.markdown("### The Event: 2026 Global Climate Summit Accord")
69
+
70
+ col1, col2 = st.columns(2)
71
+
72
+ with col1:
73
+ st.subheader("Source A (The 'Eco-Urgency' Frame)")
74
+ st.info(ARTICLE_A)
75
+ should_analyze_a = st.button("Analyze Source A")
76
+
77
+ with col2:
78
+ st.subheader("Source B (The 'Economic-Cost' Frame)")
79
+ st.warning(ARTICLE_B)
80
+ should_analyze_b = st.button("Analyze Source B")
81
+
82
+ st.divider()
83
+
84
+ if should_analyze_a or should_analyze_b:
85
+ st.markdown("### Analysis Results")
86
+ res_col1, res_col2 = st.columns(2)
87
+
88
+ if should_analyze_a:
89
+ with st.spinner("Analyzing Source A..."):
90
+ results_a = analyze_article(ARTICLE_A)
91
+ with res_col1:
92
+ st.metric("Sentiment Score (-1 to 1)", results_a["sentiment_score"])
93
+ st.write(f"**Primary Tone:** {results_a['primary_tone'].title()}")
94
+ st.write(f"**Loaded Keywords:** {', '.join(results_a['keywords'])}")
95
+
96
+ if should_analyze_b:
97
+ with st.spinner("Analyzing Source B..."):
98
+ results_b = analyze_article(ARTICLE_B)
99
+ with res_col2:
100
+ st.metric("Sentiment Score (-1 to 1)", results_b["sentiment_score"])
101
+ st.write(f"**Primary Tone:** {results_b['primary_tone'].title()}")
102
+ st.write(f"**Loaded Keywords:** {', '.join(results_b['keywords'])}")