Spaces:
Running
Running
Delete mentions_dashboard.py
Browse files- mentions_dashboard.py +0 -155
mentions_dashboard.py
DELETED
@@ -1,155 +0,0 @@
|
|
1 |
-
import time
|
2 |
-
from datetime import datetime
|
3 |
-
from collections import defaultdict
|
4 |
-
from urllib.parse import quote
|
5 |
-
|
6 |
-
import matplotlib.pyplot as plt
|
7 |
-
from transformers import pipeline
|
8 |
-
|
9 |
-
from sessions import create_session
|
10 |
-
|
11 |
-
import logging
|
12 |
-
|
13 |
-
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
|
14 |
-
|
15 |
-
|
16 |
-
# Load transformer-based sentiment model globally
|
17 |
-
sentiment_pipeline = pipeline("text-classification", model="tabularisai/multilingual-sentiment-analysis")
|
18 |
-
|
19 |
-
def extract_text_from_commentary(commentary):
|
20 |
-
import re
|
21 |
-
return re.sub(r"{.*?}", "", commentary).strip()
|
22 |
-
|
23 |
-
def classify_sentiment(text):
|
24 |
-
try:
|
25 |
-
result = sentiment_pipeline(text[:512]) # Limit to 512 chars for transformers
|
26 |
-
label = result[0]['label'].upper()
|
27 |
-
if label in ['POSITIVE', 'VERY POSITIVE']:
|
28 |
-
return 'Positive π'
|
29 |
-
elif label in ['NEGATIVE', 'VERY NEGATIVE']:
|
30 |
-
return 'Negative π'
|
31 |
-
elif label == 'NEUTRAL':
|
32 |
-
return 'Neutral π'
|
33 |
-
else:
|
34 |
-
return 'Unknown'
|
35 |
-
except Exception as e:
|
36 |
-
return 'Error'
|
37 |
-
|
38 |
-
def generate_mentions_dashboard(comm_client_id, comm_token_dict):
|
39 |
-
org_urn = "urn:li:organization:19010008"
|
40 |
-
encoded_urn = quote(org_urn, safe='')
|
41 |
-
|
42 |
-
session = create_session(comm_client_id, token=comm_token_dict)
|
43 |
-
session.headers.update({
|
44 |
-
"X-Restli-Protocol-Version": "2.0.0"
|
45 |
-
})
|
46 |
-
|
47 |
-
base_url = (
|
48 |
-
"https://api.linkedin.com/rest/organizationalEntityNotifications"
|
49 |
-
"?q=criteria"
|
50 |
-
"&actions=List(COMMENT,SHARE_MENTION)"
|
51 |
-
f"&organizationalEntity={encoded_urn}"
|
52 |
-
"&count=20"
|
53 |
-
)
|
54 |
-
|
55 |
-
all_notifications = []
|
56 |
-
start = 0
|
57 |
-
while True:
|
58 |
-
url = f"{base_url}&start={start}"
|
59 |
-
resp = session.get(url)
|
60 |
-
|
61 |
-
if resp.status_code != 200:
|
62 |
-
logging.error(f"β Error fetching notifications: {resp.status_code} - {resp.text}")
|
63 |
-
break
|
64 |
-
|
65 |
-
data = resp.json()
|
66 |
-
elements = data.get("elements", [])
|
67 |
-
all_notifications.extend(elements)
|
68 |
-
|
69 |
-
if len(elements) < data.get("paging", {}).get("count", 0):
|
70 |
-
break
|
71 |
-
|
72 |
-
start += len(elements)
|
73 |
-
time.sleep(0.5)
|
74 |
-
|
75 |
-
mention_shares = [e.get("generatedActivity") for e in all_notifications if e.get("action") == "SHARE_MENTION"]
|
76 |
-
mention_data = []
|
77 |
-
|
78 |
-
logging.info(f"Fetched {len(all_notifications)} total notifications.")
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
for share_urn in mention_shares:
|
83 |
-
if not share_urn:
|
84 |
-
continue
|
85 |
-
encoded_share_urn = quote(share_urn, safe='')
|
86 |
-
share_url = f"https://api.linkedin.com/rest/posts/{encoded_share_urn}"
|
87 |
-
response = session.get(share_url)
|
88 |
-
|
89 |
-
if response.status_code != 200:
|
90 |
-
continue
|
91 |
-
|
92 |
-
post = response.json()
|
93 |
-
commentary_raw = post.get("commentary", "")
|
94 |
-
if not commentary_raw:
|
95 |
-
continue
|
96 |
-
|
97 |
-
commentary = extract_text_from_commentary(commentary_raw)
|
98 |
-
sentiment_label = classify_sentiment(commentary)
|
99 |
-
timestamp = post.get("createdAt", 0)
|
100 |
-
dt = datetime.fromtimestamp(timestamp / 1000.0)
|
101 |
-
|
102 |
-
mention_data.append({
|
103 |
-
"date": dt,
|
104 |
-
"text": commentary,
|
105 |
-
"sentiment": sentiment_label
|
106 |
-
})
|
107 |
-
|
108 |
-
# --- HTML rendering ---
|
109 |
-
html_parts = [
|
110 |
-
"<h2 style='text-align:center;'>π£ Mentions Sentiment Dashboard</h2>"
|
111 |
-
]
|
112 |
-
|
113 |
-
for mention in mention_data:
|
114 |
-
short_text = (mention["text"][:200] + "β¦") if len(mention["text"]) > 200 else mention["text"]
|
115 |
-
html_parts.append(f"""
|
116 |
-
<div style='border:1px solid #ddd; border-radius:12px; padding:15px; margin:15px; box-shadow:2px 2px 8px rgba(0,0,0,0.05); background:#fafafa;'>
|
117 |
-
<p><strong>π
Date:</strong> {mention["date"].strftime('%Y-%m-%d')}</p>
|
118 |
-
<p style='color:#333;'>{short_text}</p>
|
119 |
-
<p><strong>Sentiment:</strong> {mention["sentiment"]}</p>
|
120 |
-
</div>
|
121 |
-
""")
|
122 |
-
|
123 |
-
html_content = "\n".join(html_parts)
|
124 |
-
|
125 |
-
# --- Plotting ---
|
126 |
-
from matplotlib.figure import Figure
|
127 |
-
fig = Figure(figsize=(12, 6))
|
128 |
-
ax = fig.subplots()
|
129 |
-
fig.subplots_adjust(bottom=0.2)
|
130 |
-
|
131 |
-
if mention_data:
|
132 |
-
# Sort by date
|
133 |
-
mention_data.sort(key=lambda x: x["date"])
|
134 |
-
|
135 |
-
date_labels = [m["date"].strftime('%Y-%m-%d') for m in mention_data]
|
136 |
-
sentiment_scores = [1 if m["sentiment"] == "Positive π" else
|
137 |
-
-1 if m["sentiment"] == "Negative π" else
|
138 |
-
0 for m in mention_data]
|
139 |
-
|
140 |
-
ax.plot(date_labels, sentiment_scores, marker='o', linestyle='-', color='#0073b1')
|
141 |
-
ax.set_title("π Mention Sentiment Over Time")
|
142 |
-
ax.set_xlabel("Date")
|
143 |
-
ax.set_ylabel("Sentiment Score (1=π, 0=π, -1=π)")
|
144 |
-
ax.tick_params(axis='x', rotation=45)
|
145 |
-
ax.grid(True, linestyle='--', alpha=0.6)
|
146 |
-
ax.set_ylim([-1.2, 1.2])
|
147 |
-
else:
|
148 |
-
ax.text(0.5, 0.5, "No mention sentiment data available.",
|
149 |
-
ha='center', va='center', transform=ax.transAxes, fontsize=12, color='grey')
|
150 |
-
ax.set_xticks([])
|
151 |
-
ax.set_yticks([])
|
152 |
-
ax.set_title("π Mention Sentiment Over Time")
|
153 |
-
|
154 |
-
return html_content, fig, mention_data
|
155 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|