Em4e commited on
Commit
3c3cecf
·
verified ·
1 Parent(s): 2817344

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +179 -151
app.py CHANGED
@@ -1,32 +1,13 @@
1
- # Import necessary libraries.
2
  import math
3
 
4
- def calculate_incremental_seo_traffic(
5
- current_position: int,
6
- target_position: int,
7
- search_volume_proxy_monthly: float,
8
- manual_ctr_improvement: float = None
9
- ) -> dict:
10
  """
11
- Calculates the estimated incremental SEO traffic based on internal linking improvements.
12
-
13
- Args:
14
- current_position (int): The current average search engine ranking position for the keywords.
15
- target_position (int): The target average search engine ranking position after internal linking.
16
- search_volume_proxy_monthly (float): Monthly proxy for search volume (e.g., GSC Impressions / 12).
17
- manual_ctr_improvement (float, optional): Manual CTR improvement as a decimal (e.g., 0.10 for 10%).
18
- If provided, this overrides the position-based CTR estimation.
19
-
20
- Returns:
21
- dict: A dictionary containing the estimated monthly and annual incremental traffic.
22
  """
23
-
24
- # Define a simplified, illustrative CTR curve based on typical observed data.
25
- # These are general approximations and can vary significantly by industry, SERP features, etc.
26
- # Source for general CTR data: Various SEO studies (e.g., Advanced Web Ranking, Sistrix, SparkToro).
27
- # This is a highly simplified model and a real-world scenario would require more granular,
28
- # often proprietary, CTR data.
29
- ctr_curve = {
30
  1: 0.28, # Position 1: ~28% CTR
31
  2: 0.15, # Position 2: ~15% CTR
32
  3: 0.10, # Position 3: ~10% CTR
@@ -39,143 +20,190 @@ def calculate_incremental_seo_traffic(
39
  10: 0.008 # Position 10: ~0.8% CTR
40
  }
41
 
42
- # Validate input positions.
43
- if not (1 <= current_position <= 10 and 1 <= target_position <= 10):
44
- print("Warning: Current and target positions should ideally be between 1 and 10 for reasonable CTR estimation.")
45
- # If positions are outside, we'll still try to use the closest available CTR or default to 0 if too far.
46
-
47
- # Ensure positions are valid for our CTR curve, clamping them if necessary.
48
- current_position = max(1, min(10, current_position))
49
- target_position = max(1, min(10, target_position))
50
-
51
- # Determine CTR improvement.
52
- if manual_ctr_improvement is not None:
53
- if not (0 <= manual_ctr_improvement <= 1):
54
- raise ValueError("Manual CTR improvement must be a decimal between 0 and 1 (e.g., 0.10 for 10%).")
55
- ctr_improvement = manual_ctr_improvement
56
- print(f"Using manual CTR improvement: {manual_ctr_improvement:.2%}")
57
- else:
58
- # Get CTRs for current and target positions.
59
- # Use .get() with a default of 0 if position is outside our defined curve (unlikely with clamping).
60
- current_ctr = ctr_curve.get(current_position, 0)
61
- target_ctr = ctr_curve.get(target_position, 0)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
 
63
- # Calculate the percentage point improvement in CTR.
64
- ctr_improvement = target_ctr - current_ctr
65
 
66
- if ctr_improvement < 0:
67
- print(f"Warning: Target position ({target_position}) has a lower estimated CTR ({target_ctr:.2%}) than current position ({current_position}) ({current_ctr:.2%}). This will result in negative incremental traffic.")
68
- else:
69
- print(f"Estimated CTR improvement from Pos {current_position} ({current_ctr:.2%}) to Pos {target_position} ({target_ctr:.2%}): {ctr_improvement:.2%} percentage points.")
70
 
71
- # Calculate incremental monthly traffic.
72
- incremental_traffic_monthly = ctr_improvement * search_volume_proxy_monthly
 
 
73
 
74
- # Calculate incremental annual traffic.
75
- incremental_traffic_annual = incremental_traffic_monthly * 12
76
 
77
- return {
78
- "incremental_traffic_monthly": incremental_traffic_monthly,
79
- "incremental_traffic_annual": incremental_traffic_annual
80
- }
81
 
82
- def main():
83
  """
84
- Main function to get user input and display the forecast.
 
85
  """
86
- print("--- Internal Linking SEO Impact Forecaster ---")
87
- print("This tool helps estimate incremental SEO traffic from internal linking improvements.")
88
- print("----------------------------------------------\n")
89
-
90
- while True:
91
- try:
92
- search_volume_input = input(
93
- "Enter the approximate *monthly total search impressions* for the keywords you are targeting "
94
- "(e.g., if you get 24 million annual impressions, enter 2000000 for monthly): "
95
- )
96
- search_volume_proxy_monthly = float(search_volume_input)
97
- if search_volume_proxy_monthly < 0:
98
- print("Monthly search volume proxy cannot be negative. Please try again.")
99
- continue
100
- break
101
- except ValueError:
102
- print("Invalid input. Please enter a numerical value for monthly search volume proxy.")
103
-
104
- while True:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  try:
106
- manual_input = input(
107
- "Do you have a specific CTR improvement percentage (e.g., 10 for 10%)? (yes/no): "
108
- ).lower().strip()
109
- if manual_input == 'yes':
110
- while True:
111
- try:
112
- manual_ctr_percent = float(input("Enter the CTR improvement percentage (e.g., 10 for 10%): "))
113
- if not (0 <= manual_ctr_percent <= 100):
114
- print("Percentage must be between 0 and 100. Please try again.")
115
- continue
116
- manual_ctr_decimal = manual_ctr_percent / 100
117
- current_pos = None # Not needed if manual CTR is provided
118
- target_pos = None # Not needed if manual CTR is provided
119
- break
120
- except ValueError:
121
- print("Invalid input. Please enter a numerical percentage.")
122
- break
123
- elif manual_input == 'no':
124
- manual_ctr_decimal = None
125
- while True:
126
- try:
127
- current_pos = int(input("Enter your current average SEO ranking position (1-10 recommended): "))
128
- if current_pos <= 0:
129
- print("Position must be a positive integer. Please try again.")
130
- continue
131
- break
132
- except ValueError:
133
- print("Invalid input. Please enter an integer for current position.")
134
-
135
- while True:
136
- try:
137
- target_pos = int(input("Enter your target average SEO ranking position (1-10 recommended): "))
138
- if target_pos <= 0:
139
- print("Position must be a positive integer. Please try again.")
140
- continue
141
- break
142
- except ValueError:
143
- print("Invalid input. Please enter an integer for target position.")
144
- break
145
  else:
146
- print("Invalid choice. Please enter 'yes' or 'no'.")
147
-
148
- except ValueError:
149
- print("Invalid input. Please try again.")
150
-
151
- try:
152
- if manual_ctr_decimal is not None:
153
- results = calculate_incremental_seo_traffic(
154
- current_position=1, # Dummy value, not used when manual_ctr_improvement is provided
155
- target_position=1, # Dummy value, not used when manual_ctr_improvement is provided
156
- search_volume_proxy_monthly=search_volume_proxy_monthly,
157
- manual_ctr_improvement=manual_ctr_decimal
158
  )
159
- else:
160
- results = calculate_incremental_seo_traffic(
161
- current_position=current_pos,
162
- target_position=target_pos,
163
- search_volume_proxy_monthly=search_volume_proxy_monthly
164
  )
165
 
166
- print("\n--- Forecast Results ---")
167
- print(f"Estimated Monthly Incremental Traffic: {math.ceil(results['incremental_traffic_monthly']):,}")
168
- print(f"Estimated Annual Incremental Traffic: {math.ceil(results['incremental_traffic_annual']):,}")
169
- print("\n--- Important Notes ---")
170
- print("- The CTR curve is a simplified model. Actual CTRs vary greatly by niche, SERP features, and keyword intent.")
171
- print("- 'Search Volume Proxy' assumes GSC Impressions / 12 is a good stand-in for actual search volume.")
172
- print("- This is a forecast; actual results may differ based on many other SEO factors.")
173
- print("- For more accurate CTR improvements, use A/B testing or historical data specific to your site.")
174
-
175
- except ValueError as e:
176
- print(f"\nError: {e}")
177
- except Exception as e:
178
- print(f"\nAn unexpected error occurred: {e}")
179
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  if __name__ == "__main__":
181
- main()
 
1
+ import streamlit as st
2
  import math
3
 
4
+ # Adhering to OOP principles by encapsulating the forecasting logic
5
+ class SEOImpactForecaster:
 
 
 
 
6
  """
7
+ A class to calculate the estimated incremental SEO traffic based on internal linking improvements.
8
+ Follows Single Responsibility Principle (SRP) by focusing solely on calculation.
 
 
 
 
 
 
 
 
 
9
  """
10
+ _CTR_CURVE = {
 
 
 
 
 
 
11
  1: 0.28, # Position 1: ~28% CTR
12
  2: 0.15, # Position 2: ~15% CTR
13
  3: 0.10, # Position 3: ~10% CTR
 
20
  10: 0.008 # Position 10: ~0.8% CTR
21
  }
22
 
23
+ def calculate_incremental_traffic(
24
+ self,
25
+ current_position: int,
26
+ target_position: int,
27
+ search_volume_proxy_monthly: float,
28
+ manual_ctr_improvement: float = None
29
+ ) -> dict:
30
+ """
31
+ Calculates the estimated incremental SEO traffic.
32
+
33
+ Args:
34
+ current_position (int): The current average search engine ranking position for the keywords.
35
+ target_position (int): The target average search engine ranking position after internal linking.
36
+ search_volume_proxy_monthly (float): Monthly proxy for search volume (e.g., GSC Impressions / 12).
37
+ manual_ctr_improvement (float, optional): Manual CTR improvement as a decimal (e.g., 0.10 for 10%).
38
+ If provided, this overrides the position-based CTR estimation.
39
+
40
+ Returns:
41
+ dict: A dictionary containing the estimated monthly and annual incremental traffic.
42
+ """
43
+ # Input validation for search_volume_proxy_monthly
44
+ if search_volume_proxy_monthly < 0:
45
+ raise ValueError("Monthly search volume proxy cannot be negative.")
46
+
47
+ # Determine CTR improvement.
48
+ if manual_ctr_improvement is not None:
49
+ if not (0 <= manual_ctr_improvement <= 1):
50
+ raise ValueError("Manual CTR improvement must be a decimal between 0 and 1 (e.g., 0.10 for 10%).")
51
+ ctr_improvement = manual_ctr_improvement
52
+ st.info(f"Using manual CTR improvement: {manual_ctr_improvement:.2%}")
53
+ else:
54
+ # Validate and clamp input positions for CTR curve
55
+ if not (1 <= current_position <= 10 and 1 <= target_position <= 10):
56
+ st.warning("Current and target positions should ideally be between 1 and 10 for reasonable CTR estimation.")
57
+
58
+ current_position = max(1, min(10, current_position))
59
+ target_position = max(1, min(10, target_position))
60
 
61
+ current_ctr = self._CTR_CURVE.get(current_position, 0)
62
+ target_ctr = self._CTR_CURVE.get(target_position, 0)
63
 
64
+ ctr_improvement = target_ctr - current_ctr
 
 
 
65
 
66
+ if ctr_improvement < 0:
67
+ st.warning(f"Target position ({target_position}) has a lower estimated CTR ({target_ctr:.2%}) than current position ({current_position}) ({current_ctr:.2%}). This will result in negative incremental traffic.")
68
+ else:
69
+ st.info(f"Estimated CTR improvement from Pos {current_position} ({current_ctr:.2%}) to Pos {target_position} ({target_ctr:.2%}): {ctr_improvement:.2%} percentage points.")
70
 
71
+ incremental_traffic_monthly = ctr_improvement * search_volume_proxy_monthly
72
+ incremental_traffic_annual = incremental_traffic_monthly * 12
73
 
74
+ return {
75
+ "incremental_traffic_monthly": incremental_traffic_monthly,
76
+ "incremental_traffic_annual": incremental_traffic_annual
77
+ }
78
 
79
+ def run_streamlit_app():
80
  """
81
+ Main function to run the Streamlit application for SEO Impact Forecaster.
82
+ Handles user input and displays results using Streamlit widgets.
83
  """
84
+ st.set_page_config(
85
+ page_title="Internal Linking SEO Impact Forecaster",
86
+ layout="centered",
87
+ initial_sidebar_state="auto"
88
+ )
89
+
90
+ st.title("Internal Linking SEO Impact Forecaster")
91
+ st.markdown("This tool helps estimate incremental SEO traffic from internal linking improvements.")
92
+ st.markdown("---")
93
+
94
+ # Input for monthly search volume proxy
95
+ search_volume_proxy_monthly = st.number_input(
96
+ "Enter the approximate *monthly total search impressions* for the keywords you are targeting:",
97
+ min_value=0.0,
98
+ value=100000.0, # Example default
99
+ step=10000.0,
100
+ format="%f",
101
+ help="This can be obtained from Google Search Console (GSC) Impressions data. "
102
+ "If you have annual impressions, divide by 12 to get a monthly figure. "
103
+ "For example, if you get 24 million annual impressions, enter 2000000 for monthly."
104
+ )
105
+
106
+ # Choose between manual CTR improvement or position-based calculation
107
+ ctr_method = st.radio(
108
+ "How do you want to determine CTR improvement?",
109
+ ("Estimate based on position change", "Enter specific CTR improvement percentage")
110
+ )
111
+
112
+ manual_ctr_decimal = None
113
+ current_pos = None
114
+ target_pos = None
115
+
116
+ if ctr_method == "Enter specific CTR improvement percentage":
117
+ manual_ctr_percent = st.number_input(
118
+ "Enter the CTR improvement percentage (e.g., 10 for 10%):",
119
+ min_value=0.0,
120
+ max_value=100.0,
121
+ value=5.0, # Example default
122
+ step=0.1,
123
+ format="%f",
124
+ help="Enter the expected increase in CTR as a percentage (e.g., 5 for a 5% increase in CTR)."
125
+ )
126
+ manual_ctr_decimal = manual_ctr_percent / 100
127
+ else:
128
+ current_pos = st.number_input(
129
+ "Enter your current average SEO ranking position (1-10 recommended):",
130
+ min_value=1,
131
+ max_value=100, # Allow higher but warn
132
+ value=5, # Example default
133
+ step=1,
134
+ help="The current average position of your targeted keywords in search results."
135
+ )
136
+ target_pos = st.number_input(
137
+ "Enter your target average SEO ranking position (1-10 recommended):",
138
+ min_value=1,
139
+ max_value=100, # Allow higher but warn
140
+ value=3, # Example default
141
+ step=1,
142
+ help="The desired average position after implementing internal linking strategies."
143
+ )
144
+
145
+ st.markdown("---")
146
+
147
+ # Calculate button
148
+ if st.button("Calculate Estimated Traffic"):
149
+ forecaster = SEOImpactForecaster()
150
  try:
151
+ if manual_ctr_decimal is not None:
152
+ results = forecaster.calculate_incremental_traffic(
153
+ current_position=1, # Dummy value, not used when manual_ctr_improvement is provided
154
+ target_position=1, # Dummy value, not used when manual_ctr_improvement is provided
155
+ search_volume_proxy_monthly=search_volume_proxy_monthly,
156
+ manual_ctr_improvement=manual_ctr_decimal
157
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  else:
159
+ results = forecaster.calculate_incremental_traffic(
160
+ current_position=current_pos,
161
+ target_position=target_pos,
162
+ search_volume_proxy_monthly=search_volume_proxy_monthly
163
+ )
164
+
165
+ st.success("### Forecast Results")
166
+ st.metric(
167
+ label="Estimated Monthly Incremental Traffic",
168
+ value=f"{math.ceil(results['incremental_traffic_monthly']):,}"
 
 
169
  )
170
+ st.metric(
171
+ label="Estimated Annual Incremental Traffic",
172
+ value=f"{math.ceil(results['incremental_traffic_annual']):,}"
 
 
173
  )
174
 
175
+ except ValueError as e:
176
+ st.error(f"Error: {e}")
177
+ except Exception as e:
178
+ st.error(f"An unexpected error occurred: {e}")
179
+
180
+ st.markdown("---")
181
+ st.info("### Important Notes")
182
+ st.markdown("""
183
+ - The CTR curve used is a **simplified model**. Actual CTRs vary greatly by niche, SERP features, and keyword intent.
184
+ - 'Search Volume Proxy' assumes Google Search Console (GSC) Impressions / 12 is a good stand-in for actual search volume.
185
+ - This is a **forecast**; actual results may differ based on many other SEO factors (e.g., content quality, competition, technical SEO).
186
+ - For more accurate CTR improvements, consider using A/B testing or historical data specific to your site.
187
+ """)
188
+
189
+ # New section for CTR Benchmarks
190
+ st.markdown("---")
191
+ st.info("### Reference CTR Benchmarks (Simplified Model)")
192
+ st.markdown("""
193
+ These are the approximate Click-Through Rates (CTRs) used in this model based on general observations.
194
+ Please note these are simplified and actual CTRs can vary significantly.
195
+ """)
196
+
197
+ # Display CTR curve in a table or list
198
+ ctr_data = SEOImpactForecaster._CTR_CURVE
199
+
200
+ st.table(
201
+ {"Position": list(ctr_data.keys()),
202
+ "Approximate CTR": [f"{v:.2%}" for v in ctr_data.values()]}
203
+ )
204
+
205
+
206
+ # To run the Streamlit app, save this code as a .py file (e.g., app.py)
207
+ # and run 'streamlit run app.py' in your terminal.
208
  if __name__ == "__main__":
209
+ run_streamlit_app()