tauman commited on
Commit
277721c
·
verified ·
1 Parent(s): ac7e43e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +264 -113
app.py CHANGED
@@ -1,146 +1,297 @@
1
  import streamlit as st
2
  import pandas as pd
 
3
  from GoogleNews import GoogleNews
4
  from transformers import pipeline
5
  import plotly.express as px
 
6
  import dateparser
7
- from datetime import datetime
8
 
9
- # --- CONFIG ---
10
- st.set_page_config(page_title="VBI Terminal", page_icon="🛡️", layout="wide")
 
 
 
 
 
11
 
12
- # Упрощенные стили, чтобы ничего не пропадало
13
  st.markdown("""
14
  <style>
15
- .report-box {
16
- background-color: #1e293b;
17
- border: 2px solid #38bdf8;
18
- padding: 20px;
19
- border-radius: 10px;
20
- color: white;
21
- margin: 10px 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  }
 
 
 
 
 
 
 
 
 
 
 
23
  </style>
24
  """, unsafe_allow_html=True)
25
 
 
26
  @st.cache_resource
27
- def load_ai():
28
- # Загрузка модели
29
  return pipeline("zero-shot-classification", model="facebook/bart-large-mnli")
30
 
31
- analyzer = load_ai()
 
 
 
 
 
 
 
 
 
 
32
 
33
- def fetch_data(query, region, depth, mode):
34
- gn = GoogleNews(lang='en', region=region)
 
 
35
  gn.clear()
36
 
37
- search_query = query
 
38
  if mode == "Social Buzz (Risk)":
39
- search_query = f'{query} scandal OR complaint OR leak'
 
 
40
 
41
- gn.search(search_query)
42
  results = gn.result()
43
 
44
- # Если нужно больше данных, берем вторую страницу
45
- if len(results) < 5:
46
  gn.getpage(2)
47
- results = gn.result()
48
 
49
- return results[:depth]
50
-
51
- def get_advice(df, score):
52
- # Логика советов
53
- if score < 50:
54
- return ["🚨 **CRITICAL:** High reputational risk. Immediate PR intervention required.",
55
- "- Release an official statement addressing top concerns.",
56
- "- Audit internal processes for technical or legal gaps."]
57
- elif score < 75:
58
- return ["⚠️ **STABILIZATION:** Reputation is volatile.",
59
- "- Increase positive social presence.",
60
- "- Address minor complaints before they escalate."]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  else:
62
- return ["✅ **HEALTHY:** Brand reputation is strong.",
63
- "- Maintain current transparency levels.",
64
- "- Use this period for aggressive market expansion."]
65
 
66
- # --- UI ---
67
- st.title("🛡️ VBI: Neural Strategy Terminal")
 
 
 
 
 
 
 
68
 
 
 
 
 
 
 
 
69
  with st.sidebar:
70
- st.header("Settings")
71
- region = st.selectbox("Region", ["KZ", "US", "GB"], index=0)
72
- mode = st.radio("Mode", ["Corporate News", "Social Buzz (Risk)"])
73
- depth = st.slider("Depth", 10, 40, 20)
 
 
74
 
75
- query = st.text_input("Enter Company/Target Name:")
 
 
76
 
77
- if st.button("RUN ANALYSIS"):
78
- if not query:
79
- st.error("Please enter a name.")
80
- else:
81
- with st.spinner("Processing neural nodes..."):
82
- try:
83
- raw = fetch_data(query, region, depth, mode)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
 
85
- if not raw:
86
- st.warning("No data found for this target. Try a different name.")
87
- else:
88
- data = []
89
- risk_labels = ["Legal", "Financial", "Technical", "PR/Social"]
90
- sent_labels = ["Positive", "Negative", "Neutral"]
91
-
92
- for item in raw:
93
- text = item.get('title', '')
94
- if not text: continue
95
-
96
- # AI Inference
97
- r_res = analyzer(text, candidate_labels=risk_labels)
98
- s_res = analyzer(text, candidate_labels=sent_labels)
99
-
100
- # Time parsing
101
- dt = dateparser.parse(item.get('date', 'now')) or datetime.now()
102
-
103
- data.append({
104
- "Time": dt,
105
- "Source": item.get('media', 'Web'),
106
- "Headline": text,
107
- "Risk": r_res['labels'][0],
108
- "Sentiment": s_res['labels'][0]
109
- })
110
-
111
- df = pd.DataFrame(data)
112
-
113
- # Metrics
114
- pos = len(df[df['Sentiment'] == "Positive"])
115
- neg = len(df[df['Sentiment'] == "Negative"])
116
- idx = 50 + (((pos - neg) / len(df)) * 50)
117
-
118
- # --- DISPLAY ---
119
- c1, c2, c3 = st.columns(3)
120
- c1.metric("Reputation Index", f"{round(idx, 1)}%")
121
- c2.metric("Signals", len(df))
122
- c3.metric("Threats", neg)
123
-
124
- # AI Advice Box
125
- st.markdown("<div class='report-box'>", unsafe_allow_html=True)
126
- st.subheader("🤖 AI Strategic Advice")
127
- for line in get_advice(df, idx):
128
- st.write(line)
129
- st.markdown("</div>", unsafe_allow_html=True)
130
-
131
- # Charts
132
- col_a, col_b = st.columns(2)
133
- with col_a:
134
- fig1 = px.pie(df, names='Sentiment', title="Sentiment", color='Sentiment',
135
- color_discrete_map={"Positive":"#10b981", "Negative":"#ef4444", "Neutral":"#64748b"})
136
- st.plotly_chart(fig1, use_container_width=True)
137
- with col_b:
138
- fig2 = px.bar(df['Risk'].value_counts().reset_index(), x='count', y='Risk', title="Risk Sectors")
139
- st.plotly_chart(fig2, use_container_width=True)
140
-
141
- # Table
142
- st.subheader("Intelligence Log")
143
- st.dataframe(df, use_container_width=True)
144
-
145
- except Exception as e:
146
- st.error(f"An error occurred: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
  import pandas as pd
3
+ import numpy as np
4
  from GoogleNews import GoogleNews
5
  from transformers import pipeline
6
  import plotly.express as px
7
+ import plotly.graph_objects as go
8
  import dateparser
9
+ from datetime import datetime, timedelta
10
 
11
+ # --- SYSTEM CONFIG ---
12
+ st.set_page_config(
13
+ page_title="VBI Terminal: Strategic AI",
14
+ page_icon="🛡️",
15
+ layout="wide",
16
+ initial_sidebar_state="collapsed"
17
+ )
18
 
19
+ # --- PROFESSIONAL UI (Cyberpunk/Terminal Style) ---
20
  st.markdown("""
21
  <style>
22
+ /* Dark Deep Background */
23
+ .stApp { background-color: #0f172a; color: #e2e8f0; }
24
+
25
+ /* Metrics Cards */
26
+ [data-testid="stMetric"] {
27
+ background-color: #1e293b !important;
28
+ border: 1px solid #334155 !important;
29
+ border-left: 5px solid #0ea5e9 !important;
30
+ border-radius: 8px;
31
+ padding: 15px !important;
32
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.5);
33
+ }
34
+ [data-testid="stMetricLabel"] p { color: #94a3b8 !important; font-size: 0.9rem !important; }
35
+ [data-testid="stMetricValue"] div { color: #f8fafc !important; font-size: 2rem !important; }
36
+
37
+ /* Advisor Box */
38
+ .advisor-box {
39
+ background-color: #334155;
40
+ border: 1px solid #10b981;
41
+ border-radius: 10px;
42
+ padding: 20px;
43
+ margin-top: 20px;
44
+ color: #f0fdf4;
45
  }
46
+ .warning-box {
47
+ background-color: #450a0a;
48
+ border: 1px solid #ef4444;
49
+ border-radius: 10px;
50
+ padding: 20px;
51
+ margin-top: 20px;
52
+ color: #fef2f2;
53
+ }
54
+
55
+ /* Tables */
56
+ .stDataFrame { border: 1px solid #334155; border-radius: 5px; }
57
  </style>
58
  """, unsafe_allow_html=True)
59
 
60
+ # --- AI ENGINE ---
61
  @st.cache_resource
62
+ def load_neural_engine():
63
+ """Load Zero-Shot Classification Model"""
64
  return pipeline("zero-shot-classification", model="facebook/bart-large-mnli")
65
 
66
+ analyzer = load_neural_engine()
67
+
68
+ # --- INTELLIGENCE ENGINE ---
69
+ def calculate_source_weight(source_name):
70
+ """Assigns weight based on source credibility (Heuristic)"""
71
+ source_name = str(source_name).lower()
72
+ if any(x in source_name for x in ['reuters', 'bloomberg', 'forbes', 'tengrinews', 'zakon', 'kapital']):
73
+ return 2.0 # High Impact Tier 1
74
+ elif any(x in source_name for x in ['blog', 'reddit', 'twitter', 'post']):
75
+ return 0.8 # Low Impact / High Noise
76
+ return 1.0 # Standard
77
 
78
+ def fetch_intelligence(query, region, depth, mode):
79
+ """Fetches real-time data using OSINT techniques."""
80
+ lang_set = 'en'
81
+ gn = GoogleNews(lang=lang_set, region=region)
82
  gn.clear()
83
 
84
+ # Advanced Query Logic
85
+ final_query = query
86
  if mode == "Social Buzz (Risk)":
87
+ final_query = f'{query} AND ("scandal" OR "fail" OR "crash" OR "complaint" OR "leak" OR "reddit" OR "opinion")'
88
+ elif mode == "Financial Intel":
89
+ final_query = f'{query} AND ("stock" OR "profit" OR "loss" OR "quarter" OR "ipo" OR "revenue")'
90
 
91
+ gn.search(final_query)
92
  results = gn.result()
93
 
94
+ if len(results) < depth:
 
95
  gn.getpage(2)
96
+ results += gn.result()
97
 
98
+ clean_data = []
99
+ seen_titles = set()
100
+
101
+ for item in results[:depth]:
102
+ title = item.get('title', '')
103
+ if title in seen_titles: continue
104
+ seen_titles.add(title)
105
+
106
+ raw_date = item.get('date', '')
107
+ parsed_date = dateparser.parse(raw_date)
108
+ if not parsed_date:
109
+ parsed_date = datetime.now()
110
+
111
+ source_label = item.get('media', 'Unknown Node')
112
+ weight = calculate_source_weight(source_label)
113
+
114
+ clean_data.append({
115
+ "Timestamp": parsed_date,
116
+ "Source": source_label,
117
+ "Headline": title,
118
+ "Link": item.get('link', '#'),
119
+ "Weight": weight
120
+ })
121
+ return clean_data
122
+
123
+ # --- STRATEGIC ADVISOR MODULE ---
124
+ def generate_ai_advice(rep_index, top_risk, volatility, sentiment_ratio):
125
+ """Generates text-based strategy based on metrics."""
126
+ advice = []
127
+ status = "STABLE"
128
+ css_class = "advisor-box"
129
+
130
+ # 1. Reputation Assessment
131
+ if rep_index < 40:
132
+ status = "CRITICAL"
133
+ css_class = "warning-box"
134
+ advice.append(f"🚨 **CRISIS MODE ACTIVATED:** Reputation Index ({round(rep_index)}%) is critically low. Immediate PR intervention required.")
135
+ elif rep_index < 60:
136
+ status = "VOLATILE"
137
+ advice.append(f"⚠️ **Caution:** Brand sentiment is mixed ({round(rep_index)}%). Monitor closely.")
138
  else:
139
+ advice.append(f"✅ **Healthy:** Strong market perception ({round(rep_index)}%). Focus on maintaining momentum.")
 
 
140
 
141
+ # 2. Risk Specific Strategy
142
+ if top_risk == "Legal/Compliance":
143
+ advice.append("⚖️ **Legal Vector:** High volume of legal discussions detected. Prepare official statements regarding compliance/lawsuits immediately.")
144
+ elif top_risk == "Technical Failure":
145
+ advice.append("🔧 **Ops Vector:** Technical complaints are trending. Issue a transparency report on uptime/fixes to reassure customers.")
146
+ elif top_risk == "Financial Risk":
147
+ advice.append("📉 **Market Vector:** Financial anxiety detected. IR (Investor Relations) should release clarifying data.")
148
+ elif top_risk == "PR Crisis":
149
+ advice.append("📣 **PR Vector:** Viral negativity detected. Do not ignore. Use 'Recall & Reframe' strategy.")
150
 
151
+ # 3. Volatility Check
152
+ if volatility > 0.3:
153
+ advice.append("🌊 **High Volatility:** Opinions are shifting rapidly. Avoid controversial statements for 48h.")
154
+
155
+ return status, "\n\n".join(advice), css_class
156
+
157
+ # --- SIDEBAR ---
158
  with st.sidebar:
159
+ st.header("🛰️ VBI: COMMAND")
160
+ target_region = st.selectbox("Geo-Node", ["KZ", "US", "GB", "RU"], index=0)
161
+ source_mode = st.radio("Signal Mode:", ["Corporate News", "Social Buzz (Risk)", "Financial Intel"])
162
+ scan_depth = st.slider("Depth", 10, 60, 30)
163
+ st.divider()
164
+ st.markdown("Created by **VBI Intelligence**")
165
 
166
+ # --- MAIN LAYOUT ---
167
+ st.title("🛡️ VBI: Strategic Brand Intelligence")
168
+ st.markdown("`v2.0` | Neural OSINT & Automated Strategy Generation")
169
 
170
+ col_search, col_btn = st.columns([4, 1])
171
+ with col_search:
172
+ target_query = st.text_input("TARGET ENTITY:", placeholder="e.g. FlyArystan, Kaspi, KazMunayGas")
173
+ with col_btn:
174
+ st.write("")
175
+ st.write("")
176
+ start_btn = st.button("🚀 INITIATE SCAN", use_container_width=True)
177
+
178
+ if start_btn and target_query:
179
+ with st.spinner(f"🛰️ Intercepting signals for '{target_query}'..."):
180
+ # 1. Fetch
181
+ raw_data = fetch_intelligence(target_query, target_region, scan_depth, source_mode)
182
+
183
+ if not raw_data:
184
+ st.error("No signals detected. System Standby.")
185
+ else:
186
+ # 2. Process
187
+ processed_data = []
188
+ risk_vectors = ["Legal/Compliance", "Financial Risk", "Technical Failure", "Market Expansion", "PR Crisis", "Customer Service"]
189
+ sentiment_cats = ["Positive", "Negative", "Neutral"]
190
+
191
+ prog = st.progress(0)
192
+ for i, item in enumerate(raw_data):
193
+ # Neural Analysis
194
+ risk_out = analyzer(item['Headline'], candidate_labels=risk_vectors)
195
+ sent_out = analyzer(item['Headline'], candidate_labels=sentiment_cats)
196
 
197
+ # Logic: If Risk Score is low, it might be just General News
198
+ top_risk = risk_out['labels'][0]
199
+ if risk_out['scores'][0] < 0.4:
200
+ top_risk = "General Noise"
201
+
202
+ # Weighted Score Calculation
203
+ sent_score = 1 if sent_out['labels'][0] == 'Positive' else -1 if sent_out['labels'][0] == 'Negative' else 0
204
+ weighted_impact = sent_score * item['Weight']
205
+
206
+ processed_data.append({
207
+ "Time": item['Timestamp'],
208
+ "Source": item['Source'],
209
+ "Headline": item['Headline'],
210
+ "Risk Category": top_risk,
211
+ "Risk Conf": risk_out['scores'][0],
212
+ "Sentiment": sent_out['labels'][0],
213
+ "Impact Score": weighted_impact
214
+ })
215
+ prog.progress((i + 1) / len(raw_data))
216
+ prog.empty()
217
+
218
+ df = pd.DataFrame(processed_data).sort_values(by='Time')
219
+
220
+ # --- ANALYTICS ENGINE ---
221
+ total_impact = df['Impact Score'].sum()
222
+ total_weight = df['Impact Score'].abs().sum() if df['Impact Score'].abs().sum() != 0 else 1
223
+
224
+ # Reputation Index (0-100)
225
+ rep_index = 50 + ((total_impact / len(df)) * 50)
226
+ rep_index = max(0, min(100, rep_index)) # Clamp 0-100
227
+
228
+ # Volatility (Std Dev of Impact)
229
+ volatility = df['Impact Score'].std()
230
+
231
+ # Top Risk
232
+ top_risk_cat = df['Risk Category'].value_counts().idxmax()
233
+
234
+ # Advice Generation
235
+ status, strategy_text, css_style = generate_ai_advice(rep_index, top_risk_cat, volatility, 0)
236
+
237
+ # --- DASHBOARD ---
238
+
239
+ # 1. STRATEGIC ADVISOR BLOCK
240
+ st.markdown(f"""
241
+ <div class="{css_style}">
242
+ <h3>🧠 AI Strategic Advisor: {status}</h3>
243
+ <div style='white-space: pre-wrap; font-size: 1.1rem;'>{strategy_text}</div>
244
+ </div>
245
+ """, unsafe_allow_html=True)
246
+
247
+ st.divider()
248
+
249
+ # 2. KEY METRICS
250
+ m1, m2, m3, m4 = st.columns(4)
251
+ m1.metric("Reputation Index", f"{round(rep_index, 1)}%", delta="Weighted Impact")
252
+ m2.metric("Signal Volume", len(df))
253
+ m3.metric("Primary Threat", top_risk_cat, delta_color="off")
254
+ m4.metric("Volatility", f"{round(volatility, 2)}", help="Higher means less stable opinions")
255
+
256
+ # 3. VISUALIZATIONS
257
+ st.subheader("📊 Visual Intelligence")
258
+
259
+ tab1, tab2 = st.tabs(["📈 Trend Dynamics", "🛡️ Risk Matrix"])
260
+
261
+ with tab1:
262
+ # Rolling Average Trend
263
+ df['MA'] = df['Impact Score'].rolling(window=3).mean()
264
+ fig_trend = go.Figure()
265
+ fig_trend.add_trace(go.Scatter(x=df['Time'], y=df['Impact Score'], mode='markers', name='Raw Signal', marker=dict(color='#94a3b8')))
266
+ fig_trend.add_trace(go.Scatter(x=df['Time'], y=df['MA'], mode='lines', name='Reputation Trend', line=dict(color='#38bdf8', width=3)))
267
+ fig_trend.update_layout(title="Reputation Stability Over Time", template="plotly_dark", paper_bgcolor='rgba(0,0,0,0)', plot_bgcolor='rgba(0,0,0,0)')
268
+ st.plotly_chart(fig_trend, use_container_width=True)
269
+
270
+ with tab2:
271
+ c1, c2 = st.columns(2)
272
+ with c1:
273
+ fig_pie = px.pie(df, names='Sentiment', title="Sentiment Distribution", color='Sentiment',
274
+ color_discrete_map={"Positive": "#10b981", "Negative": "#ef4444", "Neutral": "#64748b"})
275
+ fig_pie.update_layout(template="plotly_dark", paper_bgcolor='rgba(0,0,0,0)')
276
+ st.plotly_chart(fig_pie, use_container_width=True)
277
+ with c2:
278
+ risk_counts = df['Risk Category'].value_counts().reset_index()
279
+ risk_counts.columns = ['Risk', 'Count']
280
+ fig_bar = px.bar(risk_counts, x='Count', y='Risk', orientation='h', title="Risk Vectors",
281
+ color='Count', color_continuous_scale='Redor')
282
+ fig_bar.update_layout(template="plotly_dark", paper_bgcolor='rgba(0,0,0,0)')
283
+ st.plotly_chart(fig_bar, use_container_width=True)
284
+
285
+ # 4. INTELLIGENCE LOG
286
+ st.subheader("📡 Decrypted Signal Log")
287
+
288
+ def highlight_risk(val):
289
+ color = '#ef4444' if val in ['Legal/Compliance', 'Technical Failure', 'PR Crisis'] else '#cbd5e1'
290
+ return f'color: {color}'
291
+
292
+ display_df = df[['Time', 'Source', 'Headline', 'Risk Category', 'Sentiment', 'Impact Score']].copy()
293
+ st.dataframe(
294
+ display_df.style.map(highlight_risk, subset=['Risk Category']),
295
+ use_container_width=True,
296
+ height=400
297
+ )