mktgtech commited on
Commit
b9b50e9
·
verified ·
1 Parent(s): 5f4b467

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +92 -79
app.py CHANGED
@@ -2,7 +2,6 @@ import pandas as pd
2
  import gradio as gr
3
  import plotly.express as px
4
  import io
5
- import os
6
 
7
  # Helper function for categorization
8
  def categorize_rank(r):
@@ -17,98 +16,112 @@ def categorize_rank(r):
17
  elif r <= 100: return "51–100"
18
  else: return "100+"
19
 
20
- def process_and_viz(file):
21
  if file is None:
22
- return None, None, "Please upload a file."
23
 
24
  excel = pd.ExcelFile(file.name)
25
- processed_sheets = {}
26
- charts = []
27
-
28
- # Processing logic
29
- def process_sheet(df):
30
- df.columns = df.columns.map(lambda x: str(x).strip())
31
- non_date_cols = ["Keyword", "Avg. monthly searches"]
32
- available_non_date = [c for c in non_date_cols if c in df.columns]
33
- base_df = df[available_non_date].copy()
34
- potential_date_cols = [col for col in df.columns if col not in non_date_cols and "Unnamed" not in col]
35
-
36
- rename_dict = {}
37
- for col in potential_date_cols:
38
- try:
39
- cleaned = str(col).replace("_", " ").replace(".", " ").strip()
40
- d = pd.to_datetime(cleaned, errors="coerce", dayfirst=True)
41
- if pd.notna(d):
42
- rename_dict[col] = d.strftime("%d-%m-%Y 00:00")
43
- except: pass
44
-
45
- df.rename(columns=rename_dict, inplace=True)
46
- date_cols = [col for col in df.columns if col in rename_dict.values()]
47
- for col in date_cols:
48
- df[col] = pd.to_numeric(df[col], errors="coerce")
49
-
50
- final_df = base_df.copy()
51
- for col in date_cols:
52
- d = pd.to_datetime(col, dayfirst=True)
53
- date_label = d.strftime("%d_%b_%Y")
54
- range_col_name = f"{date_label}_Rank Range"
55
- rank_numeric_col = f"{date_label}_Rank_Numeric"
56
-
57
- final_df[date_label] = d.strftime("%d-%m-%Y 00:00")
58
- final_df[rank_numeric_col] = df[col].fillna(0).astype(int)
59
- final_df[range_col_name] = df[col].apply(categorize_rank)
60
 
61
- return final_df
 
 
 
 
 
 
 
 
 
 
 
62
 
63
- # Create an Excel file for download
64
- output_path = "Processed_Keyword_Ranking_Cleaned.xlsx"
65
- with pd.ExcelWriter(output_path, engine="openpyxl") as writer:
66
- for sheet_name in excel.sheet_names:
67
- df_sheet = pd.read_excel(excel, sheet_name=sheet_name)
68
- res_df = process_sheet(df_sheet)
69
- res_df.to_excel(writer, index=False, sheet_name=sheet_name)
70
- processed_sheets[sheet_name] = res_df
 
 
 
 
 
 
71
 
72
- # Generate Visualization for the FIRST sheet (as a preview)
73
- first_sheet_name = excel.sheet_names[0]
74
- viz_df = processed_sheets[first_sheet_name]
 
 
75
 
76
- # Identify Rank Range columns for charts
77
- range_cols = [c for c in viz_df.columns if "Rank Range" in c]
78
 
79
- fig_list = []
80
- for col in range_cols:
81
- counts = viz_df[col].value_counts().reset_index()
82
- counts.columns = ['Range', 'Keyword Count']
83
- # Ensure correct sorting for SEO buckets
84
- sort_order = ["01–03", "04–10", "11–20", "21–30", "31–40", "41–50", "51–100", "100+", "Not Talking (Intent Not Correct)"]
85
- counts['Range'] = pd.Categorical(counts['Range'], categories=sort_order, ordered=True)
86
- counts = counts.sort_values('Range')
87
-
88
- fig = px.bar(counts, x='Range', y='Keyword Count', title=f"Keywords By {col}", text_auto=True)
89
- fig_list.append(fig)
90
 
91
- return output_path, viz_df.head(20), fig_list[0] if fig_list else None
 
92
 
93
- # Gradio Interface
94
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
95
- gr.Markdown("# 📊 SEO Dashboard & Keyword Processor")
 
 
 
96
 
97
- with gr.Tab("1. Upload & Process"):
98
- file_input = gr.File(label="Upload Excel File", file_types=[".xlsx"])
99
- process_btn = gr.Button("Generate Dashboard & File", variant="primary")
100
- file_output = gr.File(label="Download Processed Excel")
 
101
 
102
- with gr.Tab("2. Visual Analytics"):
103
- gr.Markdown("### Data Preview (Top 20 Rows)")
104
- table_output = gr.DataFrame()
105
- gr.Markdown("### Ranking Distribution (Latest Month)")
106
- plot_output = gr.Plot()
107
 
108
  process_btn.click(
109
- fn=process_and_viz,
110
- inputs=file_input,
111
- outputs=[file_output, table_output, plot_output]
 
 
 
 
 
 
 
 
 
 
 
 
112
  )
113
 
114
- demo.launch()
 
 
2
  import gradio as gr
3
  import plotly.express as px
4
  import io
 
5
 
6
  # Helper function for categorization
7
  def categorize_rank(r):
 
16
  elif r <= 100: return "51–100"
17
  else: return "100+"
18
 
19
+ def process_data(file):
20
  if file is None:
21
+ return None, None, None
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
+ df.columns = df.columns.map(lambda x: str(x).strip())
28
+ non_date_cols = ["Keyword", "Avg. monthly searches"]
29
+ available_non_date = [c for c in non_date_cols if c in df.columns]
30
+
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:
34
+ try:
35
+ cleaned = str(col).replace("_", " ").replace(".", " ").strip()
36
+ d = pd.to_datetime(cleaned, errors="coerce", dayfirst=True)
37
+ if pd.notna(d):
38
+ rename_dict[col] = d.strftime("%d_%b_%Y")
39
+ except: pass
40
+
41
+ df.rename(columns=rename_dict, inplace=True)
42
+ date_cols = list(rename_dict.values())
43
+
44
+ final_df = df[available_non_date].copy()
45
+ viz_data = []
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
+ for col in date_cols:
48
+ rank_numeric_col = f"{col}_Rank_Numeric"
49
+ range_col_name = f"{col}_Rank Range"
50
+
51
+ raw_vals = pd.to_numeric(df[col], errors="coerce")
52
+ final_df[rank_numeric_col] = raw_vals.fillna(0).astype(int)
53
+ final_df[range_col_name] = raw_vals.apply(categorize_rank)
54
+
55
+ counts = final_df[range_col_name].value_counts().reset_index()
56
+ counts.columns = ['Range', 'Keyword Count']
57
+ counts['Month'] = col
58
+ viz_data.append(counts)
59
 
60
+ full_viz_df = pd.concat(viz_data)
61
+ sort_order = ["01–03", "04–10", "11–20", "21–30", "31–40", "41–50", "51–100", "100+", "Not Talking (Intent Not Correct)"]
62
+ full_viz_df['Range'] = pd.Categorical(full_viz_df['Range'], categories=sort_order, ordered=True)
63
+
64
+ fig = px.bar(
65
+ full_viz_df, x='Range', y='Keyword Count', color='Month',
66
+ title="Keyword Performance Distribution Across All Months",
67
+ text_auto=True, barmode='group',
68
+ labels={'Range': 'Rank Range', 'Keyword Count': 'Number of Keywords'}
69
+ )
70
+
71
+ fig.update_layout(clickmode='event+select')
72
+
73
+ return final_df, fig, final_df
74
 
75
+ def filter_table(evt: gr.SelectData, full_data):
76
+ # evt.value is the Rank Range bucket clicked
77
+ # evt.legend_value is the Month (color) clicked
78
+ clicked_range = evt.value
79
+ clicked_month = evt.legend_value
80
 
81
+ target_col = f"{clicked_month}_Rank Range"
 
82
 
83
+ if target_col in full_data.columns:
84
+ filtered_df = full_data[full_data[target_col] == clicked_range]
85
+ return filtered_df
86
+ return full_data
 
 
 
 
 
 
 
87
 
88
+ def reset_table(full_data):
89
+ return full_data
90
 
 
91
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
92
+ full_data_state = gr.State()
93
+
94
+ gr.Markdown("# 🚀 SEO Market Intelligence Dashboard")
95
+ gr.Markdown("### How to use: \n1. Upload your Excel. \n2. Click any bar in the chart to see which keywords are in that specific range for that month.")
96
 
97
+ with gr.Row():
98
+ file_input = gr.File(label="Upload SEO Excel", file_types=[".xlsx"])
99
+ with gr.Column():
100
+ process_btn = gr.Button("📊 Build Dashboard", variant="primary")
101
+ reset_btn = gr.Button("🔄 Reset Table Filters")
102
 
103
+ plot_output = gr.Plot(label="Ranking Distribution (Interactive)")
104
+
105
+ gr.Markdown("### 🔍 Keywords List")
106
+ table_output = gr.DataFrame(interactive=False)
 
107
 
108
  process_btn.click(
109
+ fn=process_data,
110
+ inputs=file_input,
111
+ outputs=[table_output, plot_output, full_data_state]
112
+ )
113
+
114
+ plot_output.select(
115
+ fn=filter_table,
116
+ inputs=full_data_state,
117
+ outputs=table_output
118
+ )
119
+
120
+ reset_btn.click(
121
+ fn=reset_table,
122
+ inputs=full_data_state,
123
+ outputs=table_output
124
  )
125
 
126
+ if __name__ == "__main__":
127
+ demo.launch()