GuglielmoTor commited on
Commit
9e4568f
·
verified ·
1 Parent(s): fd85262

Delete Data_Fetching_and_Rendering.py

Browse files
Files changed (1) hide show
  1. Data_Fetching_and_Rendering.py +0 -213
Data_Fetching_and_Rendering.py DELETED
@@ -1,213 +0,0 @@
1
- import json
2
- import requests
3
- import html
4
- from datetime import datetime
5
- from collections import defaultdict
6
- from transformers import pipeline
7
-
8
- from sessions import create_session
9
- from error_handling import display_error
10
- from posts_categorization import batch_summarize_and_classify
11
-
12
- import logging
13
-
14
- logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
15
-
16
- API_V2_BASE = 'https://api.linkedin.com/v2'
17
- API_REST_BASE = "https://api.linkedin.com/rest"
18
-
19
- # Load sentiment model
20
- sentiment_pipeline = pipeline("text-classification", model="tabularisai/multilingual-sentiment-analysis")
21
-
22
- def fetch_org_urn(comm_client_id, comm_token_dict):
23
- if not comm_token_dict or 'access_token' not in comm_token_dict:
24
- raise ValueError("Marketing token is missing or invalid.")
25
-
26
- session = create_session(comm_client_id, token=comm_token_dict)
27
- url = (
28
- f"{API_V2_BASE}/organizationalEntityAcls"
29
- "?q=roleAssignee&role=ADMINISTRATOR&state=APPROVED"
30
- "&projection=(elements*(*,organizationalTarget~(id,localizedName)))"
31
- )
32
-
33
- try:
34
- response = session.get(url)
35
- response.raise_for_status()
36
- except requests.exceptions.RequestException as e:
37
- status = getattr(e.response, 'status_code', 'N/A')
38
- try:
39
- details = e.response.json()
40
- except Exception:
41
- details = str(e)
42
- raise ValueError(f"Failed to fetch Organization details (Status: {status}): {details}") from e
43
-
44
- elements = response.json().get('elements')
45
- if not elements:
46
- raise ValueError("No organizations found with ADMINISTRATOR role.")
47
-
48
- org = elements[0]
49
- org_urn = org.get('organizationalTarget')
50
- org_name = org.get(next((k for k in org if k.endswith('organizationalTarget~')), {}), {}).get('localizedName')
51
-
52
- if not org_urn or not org_urn.startswith("urn:li:organization:"):
53
- raise ValueError("Invalid Organization URN.")
54
- if not org_name:
55
- org_id = org_urn.split(":")[-1]
56
- org_name = f"Organization ({org_id})"
57
-
58
- return org_urn, org_name
59
-
60
- def fetch_comments(comm_client_id, token_dict, post_urns, stats_map):
61
- from requests_oauthlib import OAuth2Session
62
- linkedin = OAuth2Session(comm_client_id, token=token_dict)
63
- linkedin.headers.update({'LinkedIn-Version': "202502"})
64
-
65
- all_comments = {}
66
- for post_urn in post_urns:
67
- if stats_map.get(post_urn, {}).get('commentCount', 0) == 0:
68
- continue
69
-
70
- try:
71
- url = f"{API_REST_BASE}/socialActions/{post_urn}/comments"
72
- response = linkedin.get(url)
73
- if response.status_code == 200:
74
- elements = response.json().get('elements', [])
75
- all_comments[post_urn] = [c.get('message', {}).get('text') for c in elements if c.get('message')]
76
- else:
77
- all_comments[post_urn] = []
78
- except Exception:
79
- all_comments[post_urn] = []
80
-
81
- return all_comments
82
-
83
- def analyze_sentiment(comments_data):
84
- results = {}
85
- for post_urn, comments in comments_data.items():
86
- sentiment_counts = defaultdict(int)
87
- total = 0
88
-
89
- for comment in comments:
90
- if not comment:
91
- continue
92
- try:
93
- result = sentiment_pipeline(comment)
94
- label = result[0]['label'].upper()
95
- if label in ['POSITIVE', 'VERY POSITIVE']:
96
- sentiment_counts['Positive 👍'] += 1
97
- elif label in ['NEGATIVE', 'VERY NEGATIVE']:
98
- sentiment_counts['Negative 👎'] += 1
99
- elif label == 'NEUTRAL':
100
- sentiment_counts['Neutral 😐'] += 1
101
- else:
102
- sentiment_counts['Unknown'] += 1
103
- total += 1
104
- except:
105
- sentiment_counts['Error'] += 1
106
-
107
- dominant = max(sentiment_counts, key=sentiment_counts.get, default='Neutral 😐')
108
- percentage = round((sentiment_counts[dominant] / total) * 100, 1) if total else 0.0
109
- results[post_urn] = {"sentiment": dominant, "percentage": percentage}
110
-
111
- return results
112
-
113
- def fetch_posts_and_stats(comm_client_id, community_token, count=10):
114
- token_dict = community_token if isinstance(community_token, dict) else {'access_token': community_token, 'token_type': 'Bearer'}
115
- session = create_session(comm_client_id, token=token_dict)
116
-
117
- #org_urn, org_name = fetch_org_urn(comm_client_id, token_dict)
118
- org_urn, org_name = "urn:li:organization:19010008", "GRLS"
119
- posts_url = f"{API_REST_BASE}/posts?author={org_urn}&q=author&count={count}&sortBy=LAST_MODIFIED"
120
-
121
- try:
122
- resp = session.get(posts_url)
123
- resp.raise_for_status()
124
- raw_posts = resp.json().get("elements", [])
125
- except requests.exceptions.RequestException as e:
126
- status = getattr(e.response, 'status_code', 'N/A')
127
- raise ValueError(f"Failed to fetch posts (Status: {status})") from e
128
-
129
- if not raw_posts:
130
- return [], org_name, {}
131
-
132
- post_urns = [p["id"] for p in raw_posts if ":share:" in p["id"] or ":ugcPost:" in p["id"]]
133
- stats_map = {}
134
-
135
- post_texts = [{"text": p["commentary"] or p.get("specificContent", {}).get("com.linkedin.ugc.ShareContent", {}).get("shareCommentaryV2", {}).get("text", "")} for p in raw_posts]
136
- structured_results = batch_summarize_and_classify(post_texts)
137
-
138
- for i in range(0, len(post_urns), 20):
139
- batch = post_urns[i:i+20]
140
- params = {'q': 'organizationalEntity', 'organizationalEntity': org_urn}
141
- for idx, urn in enumerate(batch):
142
- key = f"shares[{idx}]" if ":share:" in urn else f"ugcPosts[{idx}]"
143
- params[key] = urn
144
- try:
145
- stat_resp = session.get(f"{API_REST_BASE}/organizationalEntityShareStatistics", params=params)
146
- stat_resp.raise_for_status()
147
- for stat in stat_resp.json().get("elements", []):
148
- urn = stat.get("share") or stat.get("ugcPost")
149
- if urn:
150
- stats_map[urn] = stat.get("totalShareStatistics", {})
151
- except:
152
- continue
153
-
154
- comments = fetch_comments(comm_client_id, token_dict, post_urns, stats_map)
155
- sentiments = analyze_sentiment(comments)
156
-
157
- posts = []
158
- for post in raw_posts:
159
- post_id = post.get("id")
160
- stats = stats_map.get(post_id, {})
161
- timestamp = post.get("publishedAt") or post.get("createdAt")
162
- when = datetime.fromtimestamp(timestamp / 1000).strftime("%Y-%m-%d %H:%M") if timestamp else "Unknown"
163
-
164
- text = post.get("commentary") or post.get("specificContent", {}).get("com.linkedin.ugc.ShareContent", {}).get("shareCommentaryV2", {}).get("text") or "[No text]"
165
- text = html.escape(text[:250]).replace("\n", "<br>") + ("..." if len(text) > 250 else "")
166
-
167
- likes = stats.get("likeCount", 0)
168
- comments_count = stats.get("commentCount", 0)
169
- clicks = stats.get("clickCount", 0)
170
- shares = stats.get("shareCount", 0)
171
- impressions = stats.get("impressionCount", 0)
172
- engagement = stats.get("engagement", likes + comments_count + clicks + shares) / impressions * 100 if impressions else 0.0
173
-
174
- sentiment_info = sentiments.get(post_id, {"sentiment": "Neutral 😐", "percentage": 0.0})
175
-
176
- posts.append({
177
- "id": post_id, "when": when, "text": text, "likes": likes,
178
- "comments": comments_count, "clicks": clicks, "shares": shares,
179
- "impressions": impressions, "engagement": f"{engagement:.2f}%",
180
- "sentiment": sentiment_info["sentiment"], "sentiment_percent": sentiment_info["percentage"]
181
- })
182
- logging.info(f"Appended post data for {post_id}: Likes={likes}, Comments={comments_count}, Shares={shares}, Clicks={clicks}")
183
-
184
- for post, structured in zip(posts, structured_results):
185
- post["summary"] = structured["summary"]
186
- post["category"] = structured["category"]
187
-
188
- return posts, org_name, sentiments
189
-
190
- def render_post_cards(posts, org_name):
191
- safe_name = html.escape(org_name or "Your Organization")
192
- if not posts:
193
- return f"<h2 style='text-align:center;color:#555;'>No recent posts found for {safe_name}.</h2>"
194
-
195
- cards = [
196
- f"<div style='border:1px solid #ccc;border-radius:8px;padding:15px;width:280px;background:#fff;'>"
197
- f"<div style='font-size:0.8em;color:#666;margin-bottom:8px;'>{p['when']}</div>"
198
- f"<div style='font-size:0.95em;margin-bottom:12px;max-height:120px;overflow:auto'>{p['text']}</div>"
199
- f"<div style='font-size:0.9em;color:#333;border-top:1px solid #eee;padding-top:10px;'>"
200
- f"👁️ {p['impressions']:,} | 👍 {p['likes']:,} | 💬 {p['comments']:,} | 🔗 {p['shares']:,} | 🖱️ {p['clicks']:,}<br>"
201
- f"<strong>📈 {p['engagement']}</strong><br>"
202
- f"<span style='color:#444;'>🧠 Sentiment: <strong>{p['sentiment']}</strong> ({p['sentiment_percent']}%)</span>"
203
- f"</div></div>"
204
- for p in posts
205
- ]
206
- return f"<h2 style='text-align:center;margin-bottom:20px;'>Recent Posts for {safe_name}</h2><div style='display:flex;flex-wrap:wrap;gap:15px;justify-content:center;'>" + "".join(cards) + "</div>"
207
-
208
- def fetch_and_render_dashboard(comm_client_id, community_token):
209
- try:
210
- posts, org_name, _ = fetch_posts_and_stats(comm_client_id, community_token)
211
- return render_post_cards(posts, org_name)
212
- except Exception as e:
213
- return display_error("Dashboard Error", e).get('value', '<p style="color:red;text-align:center;">❌ An error occurred.</p>')