mktgtech commited on
Commit
7b72661
·
verified ·
1 Parent(s): 1326591

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +150 -57
app.py CHANGED
@@ -2,8 +2,10 @@ import pandas as pd
2
  import gradio as gr
3
  import plotly.express as px
4
 
5
- # Helper function for categorization
 
6
  def categorize_rank(r):
 
7
  if pd.isna(r) or r == 0 or r == "NA":
8
  return "Not Talking (Intent Not Correct)"
9
  elif r <= 3: return "01–03"
@@ -15,21 +17,17 @@ def categorize_rank(r):
15
  elif r <= 100: return "51–100"
16
  else: return "100+"
17
 
18
- def process_data(file):
19
- # Returns empty updates if no file is uploaded
20
- if file is None:
21
- return None, None, None, gr.Dropdown(choices=[]), gr.Dropdown(choices=[])
22
-
23
- excel = pd.ExcelFile(file.name)
24
- first_sheet = excel.sheet_names[0]
25
- df = pd.read_excel(file.name, sheet_name=first_sheet)
26
-
27
- # 1. Clean Columns
28
  df.columns = df.columns.map(lambda x: str(x).strip())
29
  non_date_cols = ["Keyword", "Avg. monthly searches"]
30
  available_non_date = [c for c in non_date_cols if c in df.columns]
31
 
32
- # 2. Rename Date Columns
33
  potential_date_cols = [col for col in df.columns if col not in non_date_cols and "Unnamed" not in col]
34
  rename_dict = {}
35
  for col in potential_date_cols:
@@ -43,31 +41,51 @@ def process_data(file):
43
  df.rename(columns=rename_dict, inplace=True)
44
  date_cols = list(rename_dict.values())
45
 
46
- # 3. Build Visualization Data
47
- final_df = df[available_non_date].copy()
48
- viz_data = []
49
-
50
  for col in date_cols:
51
  range_col_name = f"{col}_Rank Range"
52
  raw_vals = pd.to_numeric(df[col], errors="coerce")
53
- final_df[f"{col}_Rank_Numeric"] = raw_vals.fillna(0).astype(int)
54
- final_df[range_col_name] = raw_vals.apply(categorize_rank)
55
 
56
- counts = final_df[range_col_name].value_counts().reset_index()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  counts.columns = ['Range', 'Keyword Count']
58
- counts['Month'] = col
59
  viz_data.append(counts)
60
 
61
  full_viz_df = pd.concat(viz_data) if viz_data else pd.DataFrame()
62
 
63
- # 4. FIX SORTING
64
  correct_order = [
65
  "01–03", "04–10", "11–20", "21–30", "31–40",
66
  "41–50", "51–100", "100+", "Not Talking (Intent Not Correct)"
67
  ]
68
 
69
- # 5. Create Chart
 
 
70
  if not full_viz_df.empty:
 
71
  fig = px.bar(
72
  full_viz_df,
73
  x='Range',
@@ -75,79 +93,154 @@ def process_data(file):
75
  color='Month',
76
  barmode='group', # Side-by-side bars
77
  text_auto=True,
78
- title="Keyword Performance Distribution (Grouped)",
79
  category_orders={"Range": correct_order}
80
  )
81
- else:
82
- fig = None
83
 
84
- # Prepare Dropdown Data
85
- unique_months = list(full_viz_df['Month'].unique()) if not full_viz_df.empty else []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
 
87
- # FIX: Return new component instances instead of .update()
88
  return (
89
- final_df,
90
- fig,
91
- final_df,
92
- gr.Dropdown(choices=unique_months, value=unique_months[0] if unique_months else None),
93
- gr.Dropdown(choices=correct_order, value=correct_order[0])
 
 
94
  )
95
 
96
- def filter_table(full_data, selected_month, selected_range):
97
- if full_data is None or full_data.empty: return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
99
- # Filter by specific month AND range
100
  target_col = f"{selected_month}_Rank Range"
101
 
102
- if target_col in full_data.columns:
103
- filtered_df = full_data[full_data[target_col] == selected_range]
104
  return filtered_df
105
 
106
- return full_data
 
 
 
107
 
108
- def reset_table(full_data):
109
- return full_data
110
 
111
- # Gradio Interface
112
- with gr.Blocks() as demo:
113
- full_data_state = gr.State()
114
 
115
- gr.Markdown("# 🚀 SEO Market Intelligence Dashboard")
 
 
 
 
 
116
 
 
117
  with gr.Row():
118
- file_input = gr.File(label="Upload SEO Excel", file_types=[".xlsx"])
119
- process_btn = gr.Button("📊 Build Dashboard", variant="primary")
 
 
 
120
 
121
- # The Chart
122
  plot_output = gr.Plot(label="Keyword Distribution")
123
 
124
- gr.Markdown("### 🔍 Filter Keywords")
 
125
  with gr.Row():
126
  month_dropdown = gr.Dropdown(label="Select Month", choices=[])
127
  range_dropdown = gr.Dropdown(label="Select Rank Range", choices=[])
128
  filter_btn = gr.Button("Apply Filter")
129
- reset_btn = gr.Button("Show All")
130
 
131
  table_output = gr.DataFrame(interactive=False)
132
 
133
- # 1. Process File
 
 
134
  process_btn.click(
135
- fn=process_data,
136
  inputs=file_input,
137
- outputs=[table_output, plot_output, full_data_state, month_dropdown, range_dropdown]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  )
139
 
140
- # 2. Filter Table
141
  filter_btn.click(
142
  fn=filter_table,
143
- inputs=[full_data_state, month_dropdown, range_dropdown],
144
  outputs=table_output
145
  )
146
 
147
- # 3. Reset Table
148
  reset_btn.click(
149
  fn=reset_table,
150
- inputs=full_data_state,
151
  outputs=table_output
152
  )
153
 
 
2
  import gradio as gr
3
  import plotly.express as px
4
 
5
+ # --- 1. Helper Functions ---
6
+
7
  def categorize_rank(r):
8
+ """Categorizes the rank into SEO buckets."""
9
  if pd.isna(r) or r == 0 or r == "NA":
10
  return "Not Talking (Intent Not Correct)"
11
  elif r <= 3: return "01–03"
 
17
  elif r <= 100: return "51–100"
18
  else: return "100+"
19
 
20
+ def clean_and_process_sheet(df):
21
+ """
22
+ Takes a raw dataframe from a single sheet, cleans columns,
23
+ identifies dates, and adds Rank Range columns.
24
+ """
25
+ # Clean Headers
 
 
 
 
26
  df.columns = df.columns.map(lambda x: str(x).strip())
27
  non_date_cols = ["Keyword", "Avg. monthly searches"]
28
  available_non_date = [c for c in non_date_cols if c in df.columns]
29
 
30
+ # Identify Date Columns
31
  potential_date_cols = [col for col in df.columns if col not in non_date_cols and "Unnamed" not in col]
32
  rename_dict = {}
33
  for col in potential_date_cols:
 
41
  df.rename(columns=rename_dict, inplace=True)
42
  date_cols = list(rename_dict.values())
43
 
44
+ # Build Processed DataFrame
45
+ processed_df = df[available_non_date].copy()
46
+
 
47
  for col in date_cols:
48
  range_col_name = f"{col}_Rank Range"
49
  raw_vals = pd.to_numeric(df[col], errors="coerce")
50
+ processed_df[f"{col}_Rank_Numeric"] = raw_vals.fillna(0).astype(int)
51
+ processed_df[range_col_name] = raw_vals.apply(categorize_rank)
52
 
53
+ return processed_df
54
+
55
+ def build_chart_and_filters(df):
56
+ """
57
+ Generates the Plotly figure and filter choices for a specific dataframe.
58
+ """
59
+ if df is None or df.empty:
60
+ return None, [], []
61
+
62
+ # 1. Prepare Data for Chart
63
+ viz_data = []
64
+ # Identify all date columns by looking for "_Rank Range"
65
+ range_cols = [c for c in df.columns if "_Rank Range" in c]
66
+
67
+ for range_col in range_cols:
68
+ # Extract date from column name (e.g., "27_Oct_2025_Rank Range" -> "27_Oct_2025")
69
+ month_name = range_col.replace("_Rank Range", "")
70
+
71
+ counts = df[range_col].value_counts().reset_index()
72
  counts.columns = ['Range', 'Keyword Count']
73
+ counts['Month'] = month_name
74
  viz_data.append(counts)
75
 
76
  full_viz_df = pd.concat(viz_data) if viz_data else pd.DataFrame()
77
 
78
+ # 2. Define Sorting Order
79
  correct_order = [
80
  "01–03", "04–10", "11–20", "21–30", "31–40",
81
  "41–50", "51–100", "100+", "Not Talking (Intent Not Correct)"
82
  ]
83
 
84
+ fig = None
85
+ unique_months = []
86
+
87
  if not full_viz_df.empty:
88
+ unique_months = list(full_viz_df['Month'].unique())
89
  fig = px.bar(
90
  full_viz_df,
91
  x='Range',
 
93
  color='Month',
94
  barmode='group', # Side-by-side bars
95
  text_auto=True,
96
+ title="Keyword Performance Distribution",
97
  category_orders={"Range": correct_order}
98
  )
 
 
99
 
100
+ return fig, unique_months, correct_order
101
+
102
+
103
+ # --- 2. Gradio Event Functions ---
104
+
105
+ def process_upload_initial(file):
106
+ """
107
+ Reads ALL sheets, stores them in State, and renders the FIRST sheet.
108
+ """
109
+ if file is None:
110
+ return None, None, gr.Dropdown(choices=[]), None, None, gr.Dropdown(choices=[]), gr.Dropdown(choices=[])
111
+
112
+ excel = pd.ExcelFile(file.name)
113
+ all_sheets_data = {}
114
+
115
+ # Process every sheet and store in dictionary
116
+ for sheet_name in excel.sheet_names:
117
+ raw_df = pd.read_excel(file.name, sheet_name=sheet_name)
118
+ all_sheets_data[sheet_name] = clean_and_process_sheet(raw_df)
119
+
120
+ sheet_names = list(all_sheets_data.keys())
121
+ first_sheet_name = sheet_names[0]
122
+ first_df = all_sheets_data[first_sheet_name]
123
+
124
+ # Generate View for First Sheet
125
+ fig, months, ranges = build_chart_and_filters(first_df)
126
 
 
127
  return (
128
+ all_sheets_data, # State: All Data
129
+ first_df, # State: Current Sheet Data
130
+ gr.Dropdown(choices=sheet_names, value=first_sheet_name), # Sheet Selector
131
+ fig, # Chart
132
+ first_df, # Table
133
+ gr.Dropdown(choices=months, value=months[0] if months else None), # Month Filter
134
+ gr.Dropdown(choices=ranges, value=ranges[0] if ranges else None) # Range Filter
135
  )
136
 
137
+ def change_sheet(selected_sheet, all_data):
138
+ """
139
+ Switches the view when a new sheet is selected from dropdown.
140
+ """
141
+ if not selected_sheet or not all_data:
142
+ return None, None, None, gr.Dropdown(choices=[]), gr.Dropdown(choices=[])
143
+
144
+ new_df = all_data[selected_sheet]
145
+ fig, months, ranges = build_chart_and_filters(new_df)
146
+
147
+ return (
148
+ new_df, # Update Current Sheet State
149
+ fig, # Update Chart
150
+ new_df, # Update Table
151
+ gr.Dropdown(choices=months, value=months[0] if months else None), # Update Month Choices
152
+ gr.Dropdown(choices=ranges, value=ranges[0] if ranges else None) # Update Range Choices
153
+ )
154
+
155
+ def filter_table(current_df, selected_month, selected_range):
156
+ """
157
+ Filters the CURRENTLY active sheet's dataframe.
158
+ """
159
+ if current_df is None or current_df.empty: return None
160
 
 
161
  target_col = f"{selected_month}_Rank Range"
162
 
163
+ if target_col in current_df.columns:
164
+ filtered_df = current_df[current_df[target_col] == selected_range]
165
  return filtered_df
166
 
167
+ return current_df
168
+
169
+ def reset_table(current_df):
170
+ return current_df
171
 
 
 
172
 
173
+ # --- 3. UI Layout ---
 
 
174
 
175
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
176
+ # STATES
177
+ all_sheets_state = gr.State({}) # Stores DICTIONARY of all sheets {name: df}
178
+ current_sheet_state = gr.State(pd.DataFrame()) # Stores currently visible sheet DF
179
+
180
+ gr.Markdown("# 🚀 SEO Multi-Sheet Dashboard")
181
 
182
+ # TOP ROW: Upload & Sheet Selection
183
  with gr.Row():
184
+ file_input = gr.File(label="Upload Excel (Multiple Sheets Supported)", file_types=[".xlsx"])
185
+ with gr.Column():
186
+ process_btn = gr.Button("📊 Load File", variant="primary")
187
+ # NEW: Sheet Selector Dropdown
188
+ sheet_dropdown = gr.Dropdown(label="📑 Select Sheet to Analyze", choices=[], interactive=True)
189
 
190
+ # VISUALIZATION
191
  plot_output = gr.Plot(label="Keyword Distribution")
192
 
193
+ # FILTER ROW
194
+ gr.Markdown("### 🔍 Filter Data")
195
  with gr.Row():
196
  month_dropdown = gr.Dropdown(label="Select Month", choices=[])
197
  range_dropdown = gr.Dropdown(label="Select Rank Range", choices=[])
198
  filter_btn = gr.Button("Apply Filter")
199
+ reset_btn = gr.Button("Show All Rows")
200
 
201
  table_output = gr.DataFrame(interactive=False)
202
 
203
+ # --- EVENTS ---
204
+
205
+ # 1. Upload & Process (Loads all sheets, defaults to first)
206
  process_btn.click(
207
+ fn=process_upload_initial,
208
  inputs=file_input,
209
+ outputs=[
210
+ all_sheets_state, # Save all sheets
211
+ current_sheet_state, # Save current sheet
212
+ sheet_dropdown, # Update dropdown options
213
+ plot_output, # Show chart
214
+ table_output, # Show table
215
+ month_dropdown, # Update filters
216
+ range_dropdown
217
+ ]
218
+ )
219
+
220
+ # 2. Change Sheet (User selects "RD Monthly" etc.)
221
+ sheet_dropdown.change(
222
+ fn=change_sheet,
223
+ inputs=[sheet_dropdown, all_sheets_state],
224
+ outputs=[
225
+ current_sheet_state,
226
+ plot_output,
227
+ table_output,
228
+ month_dropdown,
229
+ range_dropdown
230
+ ]
231
  )
232
 
233
+ # 3. Filter Data (Applied to current sheet)
234
  filter_btn.click(
235
  fn=filter_table,
236
+ inputs=[current_sheet_state, month_dropdown, range_dropdown],
237
  outputs=table_output
238
  )
239
 
240
+ # 4. Reset Data
241
  reset_btn.click(
242
  fn=reset_table,
243
+ inputs=current_sheet_state,
244
  outputs=table_output
245
  )
246