mroccuper commited on
Commit
8b1fe0b
ยท
verified ยท
1 Parent(s): ad31d6e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +227 -90
app.py CHANGED
@@ -1,7 +1,12 @@
1
- # File: app.py
2
  import gradio as gr
3
  import google.generativeai as genai
4
  import textstat
 
 
 
 
 
5
 
6
  # Humanizer helper
7
  def humanize_text(text):
@@ -11,78 +16,167 @@ def humanize_text(text):
11
  "It is important to note": "Here's what you need to know",
12
  "Furthermore": "Also",
13
  "However": "But",
14
- "Therefore": "So"
 
 
 
15
  }
 
 
16
  for old, new in replacements.items():
 
17
  text = text.replace(old, new)
 
 
18
  return text
19
 
20
  # Readability analysis
21
  def analyze_readability(text):
22
- flesch = textstat.flesch_kincaid_grade(text)
23
- sentence_length = round(sum(len(sent.split()) for sent in text.split('.')) / len(text.split('.')), 1)
24
- return f"Readability Analysis:\n- Flesch-Kincaid Grade Level: {flesch}\n- Average Sentence Length: {sentence_length} words"
25
-
26
- # Title variations
27
- def generate_title_variations():
28
- return [
29
- "Kombucha Brewing 101: No Mold, No Stress",
30
- "The 7-Day Kombucha Recipe Even Newbies Can Master",
31
- "DIY Kombucha: Step-by-Step Guide to Brew Like a Pro"
32
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
  # Main generation function
35
- def generate_article(api_key, seo_plan, pov, tone, length, emotion, include_titles, include_readability, internal_links, keywords):
 
36
  try:
37
- # Configure Gemini API dynamically
38
- genai.configure(api_key=api_key.strip())
 
 
 
 
39
 
40
- # Build prompt
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  prompt = f"""
42
- You are a human guide who wants to help readers solve real problems.
43
- Write a {length} article in {pov} POV with a {tone} tone.
44
-
45
- INSTRUCTIONS:
46
- 1. Structure:
47
- - Use H1: "[H1 TITLE]"
48
- - Include all H2/H3 headings from the SEO plan
49
- - Add FAQs with simple, conversational answers
50
- - Embed internal links at natural points (use exact anchor text)
51
-
52
- 2. Style Rules:
53
- - Avoid AI clichรฉs
54
- - Use contractions (you're, don't, it's)
55
- - Add 1 relatable anecdote
56
- - Keep paragraphs short (<3 sentences)
57
-
58
- 3. Emotional Tone: {emotion}
59
- - Example: If "Confidence", say "You've got this!"
60
-
61
- 4. Keywords:
62
- - Primary keyword: "[PRIMARY KEYWORD]" (2-3x)
63
- - Semantic keywords: {keywords} (sprinkle naturally)
64
-
65
- SEO PLAN: {seo_plan}
 
 
 
 
 
 
66
  """
67
 
68
- # Gemini API call
69
- model = genai.GenerativeModel('gemini-1.5-pro')
70
  response = model.generate_content(prompt)
 
71
 
72
- # Post-processing
73
- humanized = humanize_text(response.text)
74
 
75
- if include_readability:
76
- humanized += "\n\n" + analyze_readability(humanized)
77
-
78
  if include_titles:
79
- titles = generate_title_variations()
80
- humanized = "Title Variations:\n" + "\n".join([f"{i+1}. {t}" for i, t in enumerate(titles)]) + "\n\n" + humanized
 
 
 
 
 
81
 
82
- return humanized
83
 
84
  except Exception as e:
85
- return f"๐Ÿšจ Error: {str(e)}\n\nPlease check your API key or try again later."
 
 
 
 
 
 
 
 
 
 
86
  # Enhanced theme
87
  custom_theme = gr.themes.Soft(
88
  primary_hue="blue",
@@ -98,70 +192,113 @@ custom_theme = gr.themes.Soft(
98
  )
99
 
100
  # Gradio Interface
101
- with gr.Blocks(title="SEO Article Generator", theme=custom_theme) as demo:
102
  gr.Markdown("# SEO-to-Article Generator ๐Ÿš€")
103
- gr.Markdown("Enter your Gemini API key and paste an SEO plan to generate humanized, SEO-optimized content.")
104
 
105
  with gr.Row():
106
  with gr.Column(scale=1):
107
- api_key = gr.Textbox(label="๐Ÿ”‘ Gemini API Key", type="password", placeholder="AIzaSy...your-key-here...")
 
 
 
 
 
108
 
109
- pov = gr.Dropdown(
110
- label="๐Ÿ“– Point of View",
111
- choices=["First Person", "Second Person", "Third Person"],
112
- value="First Person"
113
  )
114
 
115
- tone = gr.Dropdown(
116
- label="๐ŸŽจ Blog Tone",
117
- choices=["Friendly Guide", "Professional Expert", "Step-by-Step Teacher", "Curious Explorer"],
118
- value="Friendly Guide"
 
 
 
 
 
 
 
 
119
  )
120
 
121
- length = gr.Dropdown(
122
- label="๐Ÿ“„ Content Length",
123
- choices=["Short (500)", "Standard (800)", "Long (1200+)"],
124
- value="Standard"
125
  )
126
 
127
- emotion = gr.Dropdown(
128
- label="๐Ÿง  Emotional Tone",
129
- choices=["Trust", "Clarity", "Confidence", "Curiosity"],
130
- value="Trust"
131
  )
132
 
133
- internal_links = gr.Textbox(
134
- label="๐Ÿ”— Internal Links",
135
- placeholder="https://example.com/page1 \nhttps://example.com/page2 ",
136
- lines=3
137
  )
138
 
139
- keywords = gr.Textbox(
140
- label="๐Ÿ”‘ Extra Keywords",
141
- placeholder="SCOBY, probiotics, fermentation",
142
- lines=3
 
 
 
 
 
 
143
  )
144
 
145
- include_titles = gr.Checkbox(label="Generate Title Variations", value=True)
146
- include_readability = gr.Checkbox(label="Show Readability Analysis", value=True)
147
 
148
  with gr.Column(scale=2):
149
- seo_plan = gr.Textbox(
150
- label="๐Ÿ“„ Paste Your SEO Plan",
151
- placeholder="Primary Keyword: how to make kombucha\nSecondary Keywords: kombucha recipe...\n...",
152
- lines=15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  )
154
 
155
- submit_btn = gr.Button("โœจ Generate Article", variant="primary")
156
 
157
- output = gr.Textbox(label="๐Ÿ“ Generated Article", lines=20)
158
 
159
  submit_btn.click(
160
  fn=generate_article,
161
- inputs=[api_key, seo_plan, pov, tone, length, emotion, include_titles, include_readability, internal_links, keywords],
162
- outputs=output
 
 
 
 
 
163
  )
 
 
164
 
165
  # Launch app
166
  if __name__ == "__main__":
167
- demo.launch()
 
1
+ # File: app_SeoPlan2Article.py
2
  import gradio as gr
3
  import google.generativeai as genai
4
  import textstat
5
+ import os
6
+ # from dotenv import load_dotenv # Optional: if you want to use .env files for API key
7
+
8
+ # Optional: Load environment variables if you use a .env file
9
+ # load_dotenv()
10
 
11
  # Humanizer helper
12
  def humanize_text(text):
 
16
  "It is important to note": "Here's what you need to know",
17
  "Furthermore": "Also",
18
  "However": "But",
19
+ "Therefore": "So",
20
+ "In the realm of": "When it comes to",
21
+ "The landscape of": "Thinking about",
22
+ "It's crucial to": "You'll want to"
23
  }
24
+ # Using regex for case-insensitive replacement and whole word matching for some
25
+ import re
26
  for old, new in replacements.items():
27
+ # Basic replacement for most
28
  text = text.replace(old, new)
29
+ # Case-insensitive for some common ones if needed
30
+ # text = re.sub(r'\b' + re.escape(old) + r'\b', new, text, flags=re.IGNORECASE)
31
  return text
32
 
33
  # Readability analysis
34
  def analyze_readability(text):
35
+ try:
36
+ flesch = textstat.flesch_kincaid_grade(text)
37
+ # Robust sentence splitting and counting
38
+ sentences = [s for s in text.split('.') if s.strip()]
39
+ num_sentences = len(sentences) if len(sentences) > 0 else 1 # Avoid division by zero
40
+ words = text.split()
41
+ num_words = len(words)
42
+ sentence_length = round(num_words / num_sentences, 1) if num_sentences > 0 else 0
43
+ return f"Readability Analysis:\n- Flesch-Kincaid Grade Level: {flesch}\n- Average Sentence Length: {sentence_length} words"
44
+ except Exception as e:
45
+ return f"Readability Analysis: Error ({e})"
46
+
47
+
48
+ # Dynamic Title variations
49
+ def generate_title_variations(api_key, primary_keyword, tone, pov):
50
+ if not primary_keyword:
51
+ return ["Please provide a primary keyword for title generation."]
52
+ try:
53
+ # genai.configure(api_key=api_key) # Configure if not already configured globally
54
+ model = genai.GenerativeModel('gemini-1.5-pro-latest') # Using latest
55
+ prompt = f"""
56
+ Generate 3 compelling, SEO-friendly H1 title variations for an article about "{primary_keyword}".
57
+ The tone should be {tone} and from a {pov} perspective.
58
+ Keep them concise and engaging.
59
+ Format as a numbered list. Ensure each title is on a new line starting with number and period.
60
+ Example:
61
+ 1. Title Variation 1
62
+ 2. Title Variation 2
63
+ 3. Title Variation 3
64
+ """
65
+ response = model.generate_content(prompt)
66
+ titles = []
67
+ # Improved parsing for Gemini's potential list format
68
+ for line in response.text.strip().split('\n'):
69
+ if '. ' in line and line.strip()[0].isdigit(): # Check if line starts with "1. ", "2. " etc.
70
+ try:
71
+ titles.append(line.split('. ', 1)[1])
72
+ except IndexError:
73
+ titles.append(line) # Fallback if split fails unexpectedly
74
+ elif line.strip(): # Add non-empty lines if parsing fails
75
+ titles.append(line.strip())
76
+
77
+ return titles if titles else ["Could not generate titles. Gemini response was empty or unparseable."]
78
+ except Exception as e:
79
+ return [f"Error generating titles: {str(e)}"]
80
 
81
  # Main generation function
82
+ def generate_article(api_key_input, primary_keyword_text, seo_plan, pov, tone, length, emotion,
83
+ include_titles, include_readability, target_keywords_raw, links_to_include_raw, cta_text=""):
84
  try:
85
+ api_key_to_use = os.getenv("GEMINI_API_KEY")
86
+ if api_key_input: # Prioritize textbox input if provided
87
+ api_key_to_use = api_key_input.strip()
88
+
89
+ if not api_key_to_use:
90
+ return "๐Ÿšจ Error: API Key not provided. Please enter it in the field or set the GEMINI_API_KEY environment variable."
91
 
92
+ genai.configure(api_key=api_key_to_use)
93
+
94
+ if not primary_keyword_text:
95
+ return "๐Ÿšจ Error: Primary Keyword is required."
96
+ if not seo_plan:
97
+ return "๐Ÿšจ Error: SEO Plan / Article Outline is required."
98
+
99
+ # Process links (internal & external)
100
+ parsed_links_for_prompt = []
101
+ if links_to_include_raw:
102
+ for line in links_to_include_raw.strip().split('\n'):
103
+ line = line.strip()
104
+ if ':' in line:
105
+ anchor, url = line.split(':', 1)
106
+ anchor = anchor.strip()
107
+ url = url.strip()
108
+ if anchor and url: # Ensure both parts are non-empty
109
+ parsed_links_for_prompt.append(f"- Anchor Text: \"{anchor}\", URL: {url}")
110
+ links_instruction = "\n".join(parsed_links_for_prompt) if parsed_links_for_prompt else "No specific links provided; link naturally if opportunities arise to relevant internal or external resources."
111
+
112
+ # Process target keywords
113
+ target_keywords_list = [kw.strip() for kw in target_keywords_raw.split(',') if kw.strip()] if target_keywords_raw else []
114
+ target_keywords_instruction = f"Subtly integrate these target keywords where they fit naturally: {', '.join(target_keywords_list)}." if target_keywords_list else "No additional target keywords provided."
115
+
116
+
117
  prompt = f"""
118
+ You are an expert human writer, skilled in creating engaging, SEO-friendly content that solves real problems for readers.
119
+
120
+ ARTICLE TASK:
121
+ Write a {length} article in {pov} POV with a {tone} tone. The central focus is the primary keyword: "{primary_keyword_text}".
122
+
123
+ CORE INSTRUCTIONS:
124
+ 1. Headline (H1): Create an H1 title that is compelling, unique, and naturally incorporates the primary keyword: "{primary_keyword_text}". Do NOT just repeat the primary keyword as the H1.
125
+ 2. Structure: Adhere strictly to the headings (H2s, H3s, etc., as indicated by ##, ###) provided in the 'SEO PLAN / OUTLINE' section below. If no H-tags are in the outline, structure it logically based on the content.
126
+ 3. Content Style:
127
+ * Adopt a {emotion} emotional tone. For example, if "Confidence", use phrases like "You've got this!" or "Rest assured...".
128
+ * Write conversationally and engagingly. Use contractions (e.g., you're, it's, don't).
129
+ * Incorporate one brief, relatable anecdote relevant to the topic.
130
+ * Keep paragraphs short and highly readable (generally 1-3 sentences).
131
+ * Strictly AVOID AI clichรฉs, robotic phrasing, and overly formal language (e.g., "In conclusion", "Furthermore", "It is important to note", "The realm of", "The digital landscape"). Be original.
132
+ 4. Keyword Integration:
133
+ * Primary Keyword: "{primary_keyword_text}". Ensure this is used naturally 2-4 times throughout the article, including the H1 (or a close variation), introduction, and conclusion where appropriate.
134
+ * Target/Semantic Keywords: {target_keywords_instruction}
135
+ 5. Link Integration:
136
+ * The following links (which may be internal or external) should be embedded at natural and relevant points in the text. Use the exact anchor text and corresponding URL provided for each. Make the integration seamless.
137
+ Links to Incorporate:
138
+ {links_instruction}
139
+ 6. FAQs: If appropriate for the topic (or if specified in the outline), include a Frequently Asked Questions (FAQ) section at the end with 2-4 relevant questions and clear, concise, conversational answers.
140
+ {f"7. Call to Action: Conclude the article with this specific call to action: \"{cta_text}\"" if cta_text else "7. Call to Action: Conclude with a natural and relevant call to action (e.g., inviting comments, suggesting a next step, or encouraging sharing)."}
141
+
142
+ SEO PLAN / OUTLINE:
143
+ ---
144
+ {seo_plan}
145
+ ---
146
+
147
+ Begin the article directly with the H1 heading.
148
  """
149
 
150
+ model = genai.GenerativeModel('gemini-1.5-pro-latest')
 
151
  response = model.generate_content(prompt)
152
+ article_text = response.text
153
 
154
+ humanized_article = humanize_text(article_text)
 
155
 
156
+ final_output_parts = []
 
 
157
  if include_titles:
158
+ titles = generate_title_variations(api_key_to_use, primary_keyword_text, tone, pov)
159
+ final_output_parts.append("โœจ **Suggested Title Variations:**\n" + "\n".join([f"ย ย ย ย {i+1}. {t}" for i, t in enumerate(titles)]))
160
+
161
+ final_output_parts.append("โœ๏ธ **Generated Article:**\n" + humanized_article)
162
+
163
+ if include_readability:
164
+ final_output_parts.append("๐Ÿ“Š **Readability Analysis:**\n" + analyze_readability(humanized_article).replace("\n-", "\nย ย ย ย -")) # Indent list items
165
 
166
+ return "\n\n---\n\n".join(final_output_parts)
167
 
168
  except Exception as e:
169
+ import traceback
170
+ print(f"Detailed Error: {traceback.format_exc()}") # Print full traceback to console for debugging
171
+ error_message = f"๐Ÿšจ **Critical Error:** {str(e)}\n\n"
172
+ error_message += "This might be due to:\n"
173
+ error_message += "- Invalid or expired API Key.\n"
174
+ error_message += "- Issues with the Gemini API service.\n"
175
+ error_message += "- Malformed input in the SEO Plan or other fields.\n"
176
+ error_message += "- An unexpected problem during generation.\n\n"
177
+ error_message += "Please check your API key, ensure your inputs are correct, and try again. If the issue persists, review the console logs for more details."
178
+ return error_message
179
+
180
  # Enhanced theme
181
  custom_theme = gr.themes.Soft(
182
  primary_hue="blue",
 
192
  )
193
 
194
  # Gradio Interface
195
+ with gr.Blocks(title="SEO Article Generator", theme=custom_theme, css=".gradio-container {max-width: 1000px !important; margin: auto;} footer {display: none !important}") as demo:
196
  gr.Markdown("# SEO-to-Article Generator ๐Ÿš€")
197
+ gr.Markdown("Transform your SEO plan into humanized, optimized content. Provide your Gemini API key, primary keyword, outline, and linking strategy.")
198
 
199
  with gr.Row():
200
  with gr.Column(scale=1):
201
+ api_key_input_ui = gr.Textbox(
202
+ label="๐Ÿ”‘ Gemini API Key",
203
+ type="password",
204
+ placeholder="AIzaSy... (or leave blank if GEMINI_API_KEY env var is set)",
205
+ info="Your Google Gemini API Key. Can also be set as an environment variable."
206
+ )
207
 
208
+ primary_keyword_ui = gr.Textbox(
209
+ label="๐ŸŽฏ Primary Keyword (Required)",
210
+ placeholder="e.g., how to make kombucha",
211
+ info="The main keyword for the article."
212
  )
213
 
214
+ target_keywords_ui = gr.Textbox( # RENAMED and PURPOSE CLARIFIED
215
+ label="โž• Target/Semantic Keywords (Optional)",
216
+ placeholder="e.g., SCOBY, probiotics, fermentation, homebrew kombucha benefits",
217
+ lines=3,
218
+ info="Comma-separated list of additional keywords to include naturally."
219
+ )
220
+
221
+ links_to_include_ui = gr.Textbox( # RENAMED and PURPOSE CLARIFIED
222
+ label="๐Ÿ”— Internal & External Links to Include (Optional)",
223
+ placeholder="Benefit of Kombucha: https://example.com/kombucha-benefits\nAuthoritative Fermentation Guide: https://trusted-site.org/fermentation",
224
+ lines=4,
225
+ info="Format: Anchor Text: URL (one link per line). Both internal and external links."
226
  )
227
 
228
+ pov_ui = gr.Dropdown(
229
+ label="๐Ÿ“– Point of View",
230
+ choices=["First Person (I/We)", "Second Person (You/Your)", "Third Person (He/She/It/They)"],
231
+ value="Second Person (You/Your)"
232
  )
233
 
234
+ tone_ui = gr.Dropdown(
235
+ label="๐ŸŽจ Article Tone",
236
+ choices=["Friendly Guide", "Professional Expert", "Step-by-Step Teacher", "Curious Explorer", "Inspirational Motivator", "Witty & Humorous"],
237
+ value="Friendly Guide"
238
  )
239
 
240
+ length_ui = gr.Dropdown(
241
+ label="๐Ÿ“„ Content Length",
242
+ choices=["Short (~400-600 words)", "Standard (~700-1000 words)", "Long (~1100-1500+ words)"],
243
+ value="Standard (~700-1000 words)"
244
  )
245
 
246
+ emotion_ui = gr.Dropdown(
247
+ label="๐Ÿง  Desired Emotional Impact",
248
+ choices=["Trust", "Clarity", "Confidence", "Curiosity", "Excitement", "Empathy", "Reassurance"],
249
+ value="Confidence"
250
+ )
251
+
252
+ cta_text_ui = gr.Textbox(
253
+ label="๐Ÿ“ข Call to Action (Optional)",
254
+ placeholder="e.g., Share your kombucha brewing experiences in the comments below!",
255
+ info="A specific CTA for the end of the article. If blank, AI will generate one."
256
  )
257
 
258
+ include_titles_ui = gr.Checkbox(label="Suggest 3 Title Variations", value=True)
259
+ include_readability_ui = gr.Checkbox(label="Show Readability Analysis", value=True)
260
 
261
  with gr.Column(scale=2):
262
+ seo_plan_ui = gr.Textbox(
263
+ label="๐Ÿ“‹ SEO Plan / Article Outline (Required)",
264
+ placeholder=(
265
+ "Example:\n"
266
+ "## What is Kombucha?\n"
267
+ "### Brief History\n"
268
+ "### Why Drink It?\n\n"
269
+ "## Key Ingredients for Homemade Kombucha\n"
270
+ "### SCOBY Explained\n"
271
+ "### Best Tea for Kombucha\n\n"
272
+ "## Step-by-Step Kombucha Brewing Guide\n"
273
+ "### First Fermentation (F1)\n"
274
+ "### Second Fermentation (F2) for Flavor & Fizz\n\n"
275
+ "## Common Kombucha Problems & Solutions\n"
276
+ "### Preventing Mold\n\n"
277
+ "## FAQs\n"
278
+ " - How long does kombucha last?\n"
279
+ " - Can I use a different sugar?"
280
+ ),
281
+ lines=20, # Increased lines
282
+ info="Paste your detailed article outline. Use ## for H2, ### for H3. Include specific questions for FAQs if desired."
283
  )
284
 
285
+ submit_btn = gr.Button("โœจ Generate Article", variant="primary", elem_id="generate_button") # Added elem_id for potential CSS
286
 
287
+ output_ui = gr.Markdown(label="๐Ÿ“ Generated Output", elem_id="output_markdown") # Changed to Markdown for better formatting of titles/readability
288
 
289
  submit_btn.click(
290
  fn=generate_article,
291
+ inputs=[
292
+ api_key_input_ui, primary_keyword_ui, seo_plan_ui, pov_ui, tone_ui,
293
+ length_ui, emotion_ui, include_titles_ui, include_readability_ui,
294
+ target_keywords_ui, links_to_include_ui, cta_text_ui
295
+ ],
296
+ outputs=output_ui,
297
+ # show_progress="full" # Uncomment for Gradio's built-in progress indicator
298
  )
299
+ gr.Markdown("--- \n *Powered by Google Gemini. Ensure your API key has the Generative Language API enabled.*")
300
+
301
 
302
  # Launch app
303
  if __name__ == "__main__":
304
+ demo.launch(share=False) # Set share=True to get a public link if needed (for temporary sharing)