File size: 10,320 Bytes
adb3bbe
b560569
896ae69
a9b7f24
d252c6d
adb3bbe
538b42b
 
a9b7f24
adb3bbe
b560569
a9b7f24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
adb3bbe
 
 
 
896ae69
a9b7f24
 
adb3bbe
896ae69
a9b7f24
 
 
 
896ae69
a9b7f24
896ae69
 
 
adb3bbe
b560569
896ae69
a0b418d
a9b7f24
8a531f0
a9b7f24
 
8a531f0
 
b560569
 
a9b7f24
6e2376b
a9b7f24
 
 
 
adb3bbe
a9b7f24
 
adb3bbe
a9b7f24
adb3bbe
 
a9b7f24
adb3bbe
 
 
 
 
 
8a531f0
adb3bbe
8a531f0
4cc3230
a9b7f24
 
4cc3230
a9b7f24
6d43d2f
adb3bbe
 
 
6d43d2f
4cc3230
bff5b73
a9b7f24
 
cb4dce3
 
 
 
b8b7e00
538b42b
adb3bbe
 
 
 
a9b7f24
adb3bbe
a9b7f24
adb3bbe
 
 
 
a9b7f24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8a531f0
a9b7f24
73e88eb
 
adb3bbe
73e88eb
 
e3447a5
a9b7f24
 
 
 
 
 
 
 
 
73e88eb
adb3bbe
 
 
 
 
 
 
 
a9b7f24
adb3bbe
 
 
 
 
7ab0240
adb3bbe
 
 
4cc3230
a9b7f24
4cc3230
a9b7f24
 
 
88d3a6e
 
a9b7f24
2051c7a
 
a9b7f24
f466d89
 
a9b7f24
6d43d2f
 
a9b7f24
 
 
adb3bbe
 
 
a9b7f24
 
 
adb3bbe
06d22e5
538b42b
 
 
a9b7f24
 
538b42b
 
bff5b73
b8b7e00
538b42b
 
adb3bbe
 
a9b7f24
 
adb3bbe
a9b7f24
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# -*- coding: utf-8 -*-
import gradio as gr
import json
# Assuming these custom modules exist in your project directory or Python path
from Data_Fetching_and_Rendering import fetch_and_render_dashboard
from analytics_fetch_and_rendering import fetch_and_render_analytics
from mentions_dashboard import generate_mentions_dashboard

# Shared state for token received via POST
token_received = {"status": False, "token": None, "client_id": None}

# --- Function to get user_token from URL on load ---
def get_url_user_token(request: gr.Request):
    """
    This function is called when the Gradio app loads.
    It attempts to retrieve 'user_token' from the URL query parameters.
    """
    user_token_from_url = "user_token not found in URL" # Default message
    if request:
        # request.query_params is a dictionary-like object.
        # Example URL: https://your-gradio-app/?user_token=ABC123XYZ
        # query_params would be {'user_token': 'ABC123XYZ'}
        retrieved_token = request.query_params.get("session_id")
        if retrieved_token:
            user_token_from_url = retrieved_token
            print(f"Retrieved user_token from URL: {user_token_from_url}")
        else:
            print("user_token key was not found in the URL query parameters.")
    else:
        # This case should ideally not happen if app.load is configured correctly
        # and Gradio supplies the request object.
        print("Request object not available to get_url_user_token function.")
        user_token_from_url = "Could not access request object on load"
    return user_token_from_url

# --- Handlers for token reception (POST) and status ---
def receive_token(accessToken: str, client_id: str):
    """
    Called by a hidden POST mechanism to supply the OAuth code/token and client ID.
    """
    try:
        # The .replace("'", '"') is kept from your original code.
        # Be cautious if accessToken format can vary.
        token_dict = json.loads(accessToken.replace("'", '"'))
    except json.JSONDecodeError as e:
        print(f"Error decoding accessToken: {e}")
        token_received["status"] = False # Ensure status reflects failure
        token_received["token"] = None
        token_received["client_id"] = client_id # Keep client_id if provided
        return {
            "status": "❌ Invalid token format (POST)",
            "token": "",
            "client_id": client_id
        }
    
    token_received["status"] = True
    token_received["token"] = token_dict
    token_received["client_id"] = client_id
    print(f"Token (from POST) received successfully. Client ID: {client_id}")
    return {
        "status": "βœ… Token received (POST)",
        "token": token_dict.get("access_token", "Access token key missing"), # Display part of the token
        "client_id": client_id
    }

def check_status():
    return "βœ… Token received (POST)" if token_received["status"] else "❌ Waiting for token (POST)…"

def show_token(): # Shows token from POST
    if token_received["status"] and token_received["token"]:
        return token_received["token"].get("access_token", "Access token key missing")
    return ""

def show_client(): # Shows client_id from POST
    return token_received["client_id"] if token_received["status"] and token_received["client_id"] else ""

# --- Guarded fetch functions (using token from POST) ---
def guarded_fetch_dashboard():
    if not token_received["status"]:
        return "<p style='color:red; text-align:center;'>❌ Access denied. No token (POST) available. Please send token first.</p>"
    # token_received["client_id"] and token_received["token"] required by fetch function
    html = fetch_and_render_dashboard(
        token_received["client_id"],
        token_received["token"]
    )
    return html

def guarded_fetch_analytics():
    if not token_received["status"]:
        return (
            "<p style='color:red; text-align:center;'>❌ Access denied. No token (POST) available.</p>",
            None, None, None, None, None, None, None # Match number of outputs
        )
    # Assuming fetch_and_render_analytics returns 8 values
    count_md, plot, growth_plot, avg_post_eng_rate, interaction_metrics, eb_metrics, mentions_vol_metrics, mentions_sentiment_metrics = fetch_and_render_analytics(
        token_received["client_id"],
        token_received["token"]
    )
    return count_md, plot, growth_plot, avg_post_eng_rate, interaction_metrics, eb_metrics, mentions_vol_metrics, mentions_sentiment_metrics

def run_mentions_and_load():
    if not token_received["status"]: # Added guard similar to other functions
        return ("<p style='color:red; text-align:center;'>❌ Access denied. No token (POST) available.</p>", None)
    html, fig = generate_mentions_dashboard(
        token_received["client_id"],
        token_received["token"]
    )
    return html, fig

# --- Build the Gradio UI ---
with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
               title="LinkedIn Post Viewer & Analytics") as app:
    gr.Markdown("# πŸš€ LinkedIn Organization Post Viewer & Analytics")
    gr.Markdown("Send your OAuth token via API call (POST), then explore dashboard and analytics. URL parameters can also be displayed.")

    # Hidden elements: simulate POST endpoint for OAuth token
    hidden_token = gr.Textbox(visible=False, elem_id="hidden_token")
    hidden_client = gr.Textbox(visible=False, elem_id="hidden_client_id")
    hidden_btn = gr.Button(visible=False, elem_id="hidden_btn")

    # --- Display elements ---
    # Textbox for the user_token from URL
    url_user_token_display = gr.Textbox(label="User Token (from URL)", interactive=False, placeholder="Attempting to load from URL...")
    
    status_box = gr.Textbox(label="POST Token Status", interactive=False) # Clarified label
    token_display = gr.Textbox(label="Access Token (from POST)", interactive=False)
    client_display = gr.Textbox(label="Client ID (from POST)", interactive=False)

    # --- Load URL parameter on app start ---
    # The `get_url_user_token` function will be called when the app loads.
    # `gr.Request` is automatically passed to `get_url_user_token`.
    app.load(
        fn=get_url_user_token,
        inputs=None, # No explicit Gradio inputs needed, only gr.Request
        outputs=[url_user_token_display]
    )

    # Wire hidden POST handler for OAuth token
    hidden_btn.click(
        fn=receive_token,
        inputs=[hidden_token, hidden_client],
        outputs=[status_box, token_display, client_display]
    )

    # Polling timer to update status and displays for the POSTed token
    # Initial values are set by app.load for status_box, token_display, client_display
    # then updated by timer ticks or hidden_btn click.
    # We call check_status, show_token, show_client once at load time and then via timer.
    app.load(fn=check_status, outputs=status_box)
    app.load(fn=show_token, outputs=token_display)
    app.load(fn=show_client, outputs=client_display)
    
    timer = gr.Timer(1.0) # Poll every 1 second
    timer.tick(fn=check_status, outputs=status_box)
    timer.tick(fn=show_token, outputs=token_display)
    timer.tick(fn=show_client, outputs=client_display)

    # Tabs for functionality
    with gr.Tabs():
        with gr.TabItem("1️⃣ Dashboard"):
            gr.Markdown("View your organization's recent posts and their engagement statistics.")
            fetch_dashboard_btn = gr.Button("πŸ“Š Fetch Posts & Stats", variant="primary")
            dashboard_html = gr.HTML(value="<p style='text-align: center; color: #555;'>Waiting for POST token...</p>")
            fetch_dashboard_btn.click(
                fn=guarded_fetch_dashboard,
                inputs=[],
                outputs=[dashboard_html]
            )

        with gr.TabItem("2️⃣ Analytics"):
            gr.Markdown("View follower count and monthly gains for your organization.")
            fetch_analytics_btn = gr.Button("πŸ“ˆ Fetch Follower Analytics", variant="primary")
            
            follower_count = gr.Markdown("<p style='text-align: center; color: #555;'>Waiting for POST token...</p>")
            
            with gr.Row():
                follower_plot = gr.Plot(visible=True) # Made visible, will be empty until data
                growth_rate_plot = gr.Plot(visible=True) # Made visible

            with gr.Row():
                post_eng_rate_plot = gr.Plot(visible=True) # Made visible

            with gr.Row():
                interaction_data = gr.Plot(visible=True) # Made visible

            with gr.Row():
                eb_data = gr.Plot(visible=True) # Made visible

            with gr.Row():
                mentions_vol_data = gr.Plot(visible=True) # Made visible
                mentions_sentiment_data = gr.Plot(visible=True) # Made visible
            
            fetch_analytics_btn.click(
                fn=guarded_fetch_analytics,
                inputs=[],
                outputs=[follower_count, follower_plot, growth_rate_plot, post_eng_rate_plot, interaction_data, eb_data, mentions_vol_data, mentions_sentiment_data],
                # Show plots after click; they might need to be initially invisible if fetch_and_render_analytics can return None for plots on error
                # For simplicity, keeping them visible. Handle None returns in your fetch function if necessary.
            )

        with gr.TabItem("3️⃣ Mentions"):
            gr.Markdown("Analyze sentiment of recent posts that mention your organization.")
            fetch_mentions_btn = gr.Button("🧠 Fetch Mentions & Sentiment", variant="primary")
            mentions_html = gr.HTML(value="<p style='text-align: center; color: #555;'>Waiting for POST token...</p>") # Added placeholder
            mentions_plot = gr.Plot(visible=True) # Made visible
            fetch_mentions_btn.click(
                fn=run_mentions_and_load,
                inputs=[],
                outputs=[mentions_html, mentions_plot]
            )

# Launch the app
if __name__ == "__main__":
    # The share=True option creates a public link. Be mindful of security.
    # For embedding, you'll use the server_name and server_port you configure for your hosting.
    app.launch(server_name="0.0.0.0", server_port=7860, share=True)