GuglielmoTor commited on
Commit
3038c7b
Β·
verified Β·
1 Parent(s): fc82495

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +103 -93
app.py CHANGED
@@ -1,9 +1,8 @@
1
  # -*- coding: utf-8 -*-
2
  import gradio as gr
3
  import json
4
- import requests # Added for API calls
5
- import os # Added for environment variables
6
- import urllib.parse # Added for URL encoding (though requests handles params well)
7
 
8
  # Assuming these custom modules exist in your project directory or Python path
9
  from Data_Fetching_and_Rendering import fetch_and_render_dashboard
@@ -11,94 +10,113 @@ from analytics_fetch_and_rendering import fetch_and_render_analytics
11
  from mentions_dashboard import generate_mentions_dashboard
12
 
13
  # Import the function from your utils file
14
- from gradio_utils import get_url_user_token # Assuming gradio_utils.py is in the same directory
 
15
  from Bubble_API_Calls import fetch_linkedin_token_from_bubble
16
 
17
- # Shared state for token received via POST or Bubble
18
- token_received = {"status": False, "token": None, "client_id": None}
 
 
 
 
 
 
 
 
 
 
19
 
20
- # --- Handlers for token reception (POST) and status ---
21
- def receive_token(accessToken: str, client_id: str):
22
  """
23
- Called by a hidden POST mechanism to supply the OAuth code/token and client ID.
 
 
 
 
 
24
  """
25
- try:
26
- token_dict = json.loads(accessToken.replace("'", '"'))
27
- except json.JSONDecodeError as e:
28
- print(f"Error decoding accessToken (POST): {e}")
29
- token_received["status"] = False
30
- token_received["token"] = None
31
- token_received["client_id"] = client_id
32
- return "❌ Invalid token format (POST)", "", client_id
33
-
34
- token_received["status"] = True
35
- token_received["token"] = token_dict # This should be the dict like {"access_token": "value"}
36
- token_received["client_id"] = client_id
37
- print(f"Token (from POST) received successfully. Client ID: {client_id}")
38
- # Update status box, token display, client display directly
39
- return check_status(), show_token(), show_client()
40
-
41
-
42
- def check_status():
43
- return "βœ… Token available" if token_received["status"] else "❌ Waiting for token…"
44
-
45
- def show_token(): # Shows access_token if available
46
- if token_received["status"] and token_received["token"] and isinstance(token_received["token"], dict):
47
- return token_received["token"].get("access_token", "Access token key missing in dict")
48
- elif token_received["status"] and token_received["token"]: # If token is a raw string (should not happen with new logic)
49
- return str(token_received["token"]) # Fallback, but ideally token_received["token"] is always a dict if status is True
50
- return ""
51
-
52
- def show_client():
53
- return token_received["client_id"] if token_received["status"] and token_received["client_id"] else ""
54
-
55
-
56
-
57
- # --- Guarded fetch functions (using token from POST or Bubble) ---
58
- # These functions expect token_received["token"] to be a dictionary
59
- # like {"access_token": "actual_token_value", ...}
60
- def guarded_fetch_dashboard():
61
- if not token_received["status"]:
62
- return "<p style='color:red; text-align:center;'>❌ Access denied. No token available. Please send token first or ensure URL token is valid.</p>"
 
63
  html = fetch_and_render_dashboard(
64
- token_received["client_id"],
65
- token_received["token"]
66
  )
67
  return html
68
 
69
- def guarded_fetch_analytics():
70
- if not token_received["status"]:
71
  return (
72
  "<p style='color:red; text-align:center;'>❌ Access denied. No token available.</p>",
73
  None, None, None, None, None, None, None
74
  )
 
 
75
  count_md, plot, growth_plot, avg_post_eng_rate, interaction_metrics, eb_metrics, mentions_vol_metrics, mentions_sentiment_metrics = fetch_and_render_analytics(
76
- token_received["client_id"],
77
- token_received["token"]
78
  )
79
  return count_md, plot, growth_plot, avg_post_eng_rate, interaction_metrics, eb_metrics, mentions_vol_metrics, mentions_sentiment_metrics
80
 
81
- def run_mentions_and_load():
82
- if not token_received["status"]:
83
  return ("<p style='color:red; text-align:center;'>❌ Access denied. No token available.</p>", None)
84
  html, fig = generate_mentions_dashboard(
85
- token_received["client_id"],
86
- token_received["token"]
87
  )
88
  return html, fig
89
 
90
  # --- Build the Gradio UI ---
91
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
92
  title="LinkedIn Post Viewer & Analytics") as app:
93
- gr.Markdown("# πŸš€ LinkedIn Organization Post Viewer & Analytics")
94
- gr.Markdown("Token can be supplied via URL parameter (for Bubble.io lookup) or hidden POST. Then explore dashboard and analytics.")
 
 
95
 
96
- # Hidden elements: simulate POST endpoint for OAuth token
97
- hidden_token_input = gr.Textbox(visible=False, elem_id="hidden_token")
98
- hidden_client_input = gr.Textbox(visible=False, elem_id="hidden_client_id")
99
- hidden_btn = gr.Button(visible=False, elem_id="hidden_btn")
100
 
101
- # --- Display elements ---
102
  url_user_token_display = gr.Textbox(
103
  label="User Token (from URL - Hidden)",
104
  interactive=False,
@@ -106,39 +124,38 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
106
  visible=False
107
  )
108
 
109
- parsed_token_dict = gr.Textbox(label="Bubble API Call Status", interactive=False, placeholder="Waiting for URL token...")
 
110
 
 
111
  status_box = gr.Textbox(label="Overall Token Status", interactive=False)
112
- token_display = gr.Textbox(label="Access Token (Active)", interactive=False)
113
  client_display = gr.Textbox(label="Client ID (Active)", interactive=False)
 
114
 
115
  # --- Load URL parameter on app start & Link to Bubble Fetch ---
116
  app.load(
117
  fn=get_url_user_token,
118
- inputs=None,
119
  outputs=[url_user_token_display]
120
  )
121
 
 
 
122
  url_user_token_display.change(
123
- fn=fetch_linkedin_token_from_bubble,
124
- inputs=[url_user_token_display],
125
- outputs=[parsed_token_dict]
126
- )
127
-
128
- hidden_btn.click(
129
- fn=receive_token,
130
- inputs=[hidden_token_input, hidden_client_input],
131
- outputs=[status_box, token_display, client_display]
132
  )
133
 
134
- app.load(fn=check_status, outputs=status_box)
135
- app.load(fn=show_token, outputs=token_display)
136
- app.load(fn=show_client, outputs=client_display)
137
 
138
- timer = gr.Timer(2.0)
139
- timer.tick(fn=check_status, outputs=status_box)
140
- timer.tick(fn=show_token, outputs=token_display)
141
- timer.tick(fn=show_client, outputs=client_display)
 
142
 
143
  # Tabs for functionality
144
  with gr.Tabs():
@@ -148,7 +165,7 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
148
  dashboard_html = gr.HTML(value="<p style='text-align: center; color: #555;'>Waiting for token...</p>")
149
  fetch_dashboard_btn.click(
150
  fn=guarded_fetch_dashboard,
151
- inputs=[],
152
  outputs=[dashboard_html]
153
  )
154
 
@@ -161,23 +178,19 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
161
  with gr.Row():
162
  follower_plot = gr.Plot(visible=True)
163
  growth_rate_plot = gr.Plot(visible=True)
164
-
165
  with gr.Row():
166
  post_eng_rate_plot = gr.Plot(visible=True)
167
-
168
  with gr.Row():
169
  interaction_data = gr.Plot(visible=True)
170
-
171
  with gr.Row():
172
  eb_data = gr.Plot(visible=True)
173
-
174
  with gr.Row():
175
  mentions_vol_data = gr.Plot(visible=True)
176
  mentions_sentiment_data = gr.Plot(visible=True)
177
 
178
  fetch_analytics_btn.click(
179
  fn=guarded_fetch_analytics,
180
- inputs=[],
181
  outputs=[follower_count, follower_plot, growth_rate_plot, post_eng_rate_plot, interaction_data, eb_data, mentions_vol_data, mentions_sentiment_data]
182
  )
183
 
@@ -188,14 +201,11 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
188
  mentions_plot = gr.Plot(visible=True)
189
  fetch_mentions_btn.click(
190
  fn=run_mentions_and_load,
191
- inputs=[],
192
  outputs=[mentions_html, mentions_plot]
193
  )
194
 
195
  # Launch the app
196
  if __name__ == "__main__":
197
  # Ensure the 'Bubble_API' environment variable is set where this app is run.
198
- # For local testing, you can set it in your terminal before running:
199
- # export Bubble_API="YOUR_ACTUAL_BUBBLE_API_KEY"
200
- # python app.py
201
  app.launch(server_name="0.0.0.0", server_port=7860, share=True)
 
1
  # -*- coding: utf-8 -*-
2
  import gradio as gr
3
  import json
4
+ # requests, os, urllib.parse are used by Bubble_API_Calls.py, not directly here anymore
5
+ # but good to keep if you add other direct calls later.
 
6
 
7
  # Assuming these custom modules exist in your project directory or Python path
8
  from Data_Fetching_and_Rendering import fetch_and_render_dashboard
 
10
  from mentions_dashboard import generate_mentions_dashboard
11
 
12
  # Import the function from your utils file
13
+ from gradio_utils import get_url_user_token
14
+ # Import the Bubble API call function (ensure filename matches: Bubble_API_Calls.py)
15
  from Bubble_API_Calls import fetch_linkedin_token_from_bubble
16
 
17
+ # --- Session State dependent functions ---
18
+ def check_token_status(current_token_state):
19
+ """Checks if a valid token exists in the session state."""
20
+ if current_token_state and current_token_state.get("token") and current_token_state.get("status"):
21
+ return "βœ… Token available"
22
+ return "❌ Waiting for token…"
23
+
24
+ def get_active_client_id(current_token_state):
25
+ """Gets the client_id from the session state if a token is available."""
26
+ if current_token_state and current_token_state.get("token") and current_token_state.get("status"):
27
+ return current_token_state.get("client_id", "Client ID not set")
28
+ return ""
29
 
30
+ # --- Function to process and store token from Bubble ---
31
+ def process_and_store_bubble_token(url_user_token_str, current_token_state):
32
  """
33
+ Fetches token from Bubble, updates session state, and returns UI update values.
34
+ Args:
35
+ url_user_token_str: The token string extracted from the URL.
36
+ current_token_state: The current session state for the token.
37
+ Returns:
38
+ Tuple: (bubble_api_status_msg, overall_status, client_id_display, updated_token_state)
39
  """
40
+ bubble_api_status_msg = "Waiting for URL token..."
41
+ new_token_state = current_token_state.copy() if current_token_state else {"status": False, "token": None, "client_id": None}
42
+
43
+ if not url_user_token_str or "not found" in url_user_token_str or "Could not access" in url_user_token_str:
44
+ bubble_api_status_msg = f"ℹ️ No valid user token from URL to query Bubble. ({url_user_token_str})"
45
+ # Even if no valid URL token, return current status based on existing state
46
+ return bubble_api_status_msg, check_token_status(new_token_state), get_active_client_id(new_token_state), new_token_state
47
+
48
+ print(f"Attempting to fetch token from Bubble with state: {url_user_token_str}")
49
+ parsed_token_dict = fetch_linkedin_token_from_bubble(url_user_token_str)
50
+
51
+ if parsed_token_dict and isinstance(parsed_token_dict, dict) and "access_token" in parsed_token_dict:
52
+ new_token_state["status"] = True
53
+ new_token_state["token"] = parsed_token_dict
54
+ new_token_state["client_id"] = f"Bubble (state: {url_user_token_str})"
55
+ bubble_api_status_msg = f"βœ… Token successfully fetched from Bubble for state: {url_user_token_str}"
56
+ print(bubble_api_status_msg)
57
+ else:
58
+ # Fetch failed or no valid token returned, keep previous state or mark as no token
59
+ # If you want a Bubble failure to explicitly clear any old token:
60
+ # new_token_state["status"] = False
61
+ # new_token_state["token"] = None
62
+ # new_token_state["client_id"] = None
63
+ # For now, it just means the Bubble fetch didn't provide a new one.
64
+ bubble_api_status_msg = f"❌ Failed to fetch a valid token from Bubble for state: {url_user_token_str}. Check console logs from Bubble_API_Calls.py."
65
+ print(bubble_api_status_msg)
66
+ # If the goal is that ONLY a successful bubble fetch provides a token, then reset status:
67
+ new_token_state["status"] = False
68
+ new_token_state["token"] = None
69
+ # client_id might be kept or cleared based on preference
70
+ # new_token_state["client_id"] = f"Bubble fetch failed (state: {url_user_token_str})"
71
+
72
+
73
+ return bubble_api_status_msg, check_token_status(new_token_state), get_active_client_id(new_token_state), new_token_state
74
+
75
+ # --- Guarded fetch functions (now use token_state) ---
76
+ def guarded_fetch_dashboard(current_token_state):
77
+ if not (current_token_state and current_token_state.get("status") and current_token_state.get("token")):
78
+ return "<p style='color:red; text-align:center;'>❌ Access denied. No token available.</p>"
79
  html = fetch_and_render_dashboard(
80
+ current_token_state["client_id"],
81
+ current_token_state["token"]
82
  )
83
  return html
84
 
85
+ def guarded_fetch_analytics(current_token_state):
86
+ if not (current_token_state and current_token_state.get("status") and current_token_state.get("token")):
87
  return (
88
  "<p style='color:red; text-align:center;'>❌ Access denied. No token available.</p>",
89
  None, None, None, None, None, None, None
90
  )
91
+ client_id = current_token_state["client_id"]
92
+ token_data = current_token_state["token"]
93
  count_md, plot, growth_plot, avg_post_eng_rate, interaction_metrics, eb_metrics, mentions_vol_metrics, mentions_sentiment_metrics = fetch_and_render_analytics(
94
+ client_id,
95
+ token_data
96
  )
97
  return count_md, plot, growth_plot, avg_post_eng_rate, interaction_metrics, eb_metrics, mentions_vol_metrics, mentions_sentiment_metrics
98
 
99
+ def run_mentions_and_load(current_token_state):
100
+ if not (current_token_state and current_token_state.get("status") and current_token_state.get("token")):
101
  return ("<p style='color:red; text-align:center;'>❌ Access denied. No token available.</p>", None)
102
  html, fig = generate_mentions_dashboard(
103
+ current_token_state["client_id"],
104
+ current_token_state["token"]
105
  )
106
  return html, fig
107
 
108
  # --- Build the Gradio UI ---
109
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
110
  title="LinkedIn Post Viewer & Analytics") as app:
111
+
112
+ # Session state to store the token info
113
+ # Initial value is a dictionary representing an unauthenticated state.
114
+ token_state = gr.State(value={"status": False, "token": None, "client_id": None})
115
 
116
+ gr.Markdown("# πŸš€ LinkedIn Organization Post Viewer & Analytics")
117
+ gr.Markdown("Token is supplied via URL parameter for Bubble.io lookup. Then explore dashboard and analytics.")
 
 
118
 
119
+ # Hidden textbox to capture token from URL
120
  url_user_token_display = gr.Textbox(
121
  label="User Token (from URL - Hidden)",
122
  interactive=False,
 
124
  visible=False
125
  )
126
 
127
+ # Display for Bubble API call status
128
+ bubble_api_status_display = gr.Textbox(label="Bubble API Call Status", interactive=False, placeholder="Waiting for URL token...")
129
 
130
+ # Overall status displays
131
  status_box = gr.Textbox(label="Overall Token Status", interactive=False)
 
132
  client_display = gr.Textbox(label="Client ID (Active)", interactive=False)
133
+ # Note: The textbox for displaying the actual token is removed.
134
 
135
  # --- Load URL parameter on app start & Link to Bubble Fetch ---
136
  app.load(
137
  fn=get_url_user_token,
138
+ inputs=None, # get_url_user_token takes gr.Request implicitly
139
  outputs=[url_user_token_display]
140
  )
141
 
142
+ # When the hidden url_user_token_display changes (due to app.load),
143
+ # trigger the Bubble API call and update session state.
144
  url_user_token_display.change(
145
+ fn=process_and_store_bubble_token,
146
+ inputs=[url_user_token_display, token_state], # Pass current state
147
+ outputs=[bubble_api_status_display, status_box, client_display, token_state] # Update UI and state
 
 
 
 
 
 
148
  )
149
 
150
+ # Initial UI state based on initial token_state
151
+ app.load(fn=check_token_status, inputs=[token_state], outputs=status_box)
152
+ app.load(fn=get_active_client_id, inputs=[token_state], outputs=client_display)
153
 
154
+ # Timer to periodically update status (e.g., if token could expire or be managed externally)
155
+ # This might be less critical if token acquisition is only at the start via URL.
156
+ timer = gr.Timer(5.0) # Poll every 5 seconds, adjust as needed
157
+ timer.tick(fn=check_token_status, inputs=[token_state], outputs=status_box)
158
+ timer.tick(fn=get_active_client_id, inputs=[token_state], outputs=client_display)
159
 
160
  # Tabs for functionality
161
  with gr.Tabs():
 
165
  dashboard_html = gr.HTML(value="<p style='text-align: center; color: #555;'>Waiting for token...</p>")
166
  fetch_dashboard_btn.click(
167
  fn=guarded_fetch_dashboard,
168
+ inputs=[token_state], # Pass session state
169
  outputs=[dashboard_html]
170
  )
171
 
 
178
  with gr.Row():
179
  follower_plot = gr.Plot(visible=True)
180
  growth_rate_plot = gr.Plot(visible=True)
 
181
  with gr.Row():
182
  post_eng_rate_plot = gr.Plot(visible=True)
 
183
  with gr.Row():
184
  interaction_data = gr.Plot(visible=True)
 
185
  with gr.Row():
186
  eb_data = gr.Plot(visible=True)
 
187
  with gr.Row():
188
  mentions_vol_data = gr.Plot(visible=True)
189
  mentions_sentiment_data = gr.Plot(visible=True)
190
 
191
  fetch_analytics_btn.click(
192
  fn=guarded_fetch_analytics,
193
+ inputs=[token_state], # Pass session state
194
  outputs=[follower_count, follower_plot, growth_rate_plot, post_eng_rate_plot, interaction_data, eb_data, mentions_vol_data, mentions_sentiment_data]
195
  )
196
 
 
201
  mentions_plot = gr.Plot(visible=True)
202
  fetch_mentions_btn.click(
203
  fn=run_mentions_and_load,
204
+ inputs=[token_state], # Pass session state
205
  outputs=[mentions_html, mentions_plot]
206
  )
207
 
208
  # Launch the app
209
  if __name__ == "__main__":
210
  # Ensure the 'Bubble_API' environment variable is set where this app is run.
 
 
 
211
  app.launch(server_name="0.0.0.0", server_port=7860, share=True)