Natwar commited on
Commit
fe98774
Β·
verified Β·
1 Parent(s): f1c241e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +148 -198
app.py CHANGED
@@ -1,63 +1,7 @@
1
- # -*- coding: utf-8 -*-
2
- """Seo_Blog_Finale.ipynb
3
-
4
- Automatically generated by Colab.
5
-
6
- Original file is located at
7
- https://colab.research.google.com/drive/1QI2D7IKD5MNQKverCCzA9-Mq_pkxD4PX
8
- """
9
-
10
- # ======================== INSTALL DEPENDENCIES ========================
11
- # Install required packages
12
- def install_package(package):
13
- try:
14
- import subprocess
15
- import sys
16
- subprocess.check_call([sys.executable, "-m", "pip", "install", package])
17
- print(f"βœ… {package} installed successfully")
18
- except:
19
- print(f"⚠️ Could not install {package}")
20
-
21
- packages = [
22
- "openai", "requests", "beautifulsoup4", "pandas", "gradio", "openpyxl", "spacy", "python-docx", "google-search-results"
23
- ]
24
- for package in packages:
25
- try:
26
- if package == "openai":
27
- from openai import OpenAI
28
- elif package == "requests":
29
- import requests
30
- elif package == "beautifulsoup4":
31
- import bs4
32
- elif package == "pandas":
33
- import pandas
34
- elif package == "gradio":
35
- import gradio
36
- elif package == "openpyxl":
37
- import openpyxl
38
- elif package == "spacy":
39
- import spacy
40
- elif package == "python-docx":
41
- from docx import Document
42
- elif package == "google-search-results":
43
- from serpapi import GoogleSearch
44
- except ImportError:
45
- install_package(package)
46
-
47
- # Download spaCy model if needed
48
- try:
49
- import spacy
50
- nlp = spacy.load("en_core_web_sm")
51
- except:
52
- print("Installing spaCy English model...")
53
- import subprocess
54
- import sys
55
- subprocess.check_call([sys.executable, "-m", "spacy", "download", "en_core_web_sm"])
56
- nlp = spacy.load("en_core_web_sm")
57
-
58
  import subprocess
59
  import sys
60
- import os # MODIFIED: Ensure 'os' is imported
61
  import re
62
  import time
63
  import json
@@ -77,6 +21,10 @@ from docx.oxml import OxmlElement
77
  from docx.oxml.ns import qn
78
  from itertools import cycle
79
  from serpapi import GoogleSearch
 
 
 
 
80
 
81
  # ======================== CONFIGURE OPENAI & SERPAPI ========================
82
  # MODIFIED: Get API keys from Hugging Face Secrets and add error handling
@@ -92,7 +40,6 @@ if not serpapi_key:
92
  client = OpenAI(api_key=openai_api_key)
93
  SERPAPI_KEY = serpapi_key
94
 
95
-
96
  # ======================== BLOG CONTENT TYPES ========================
97
  BLOG_TYPES = {
98
  "How-To Guide πŸ“š": "step-by-step tutorials",
@@ -141,6 +88,7 @@ Now, generate the FAQs for the current blog topic following these rules and the
141
  """
142
 
143
  BLOG_STRUCTURES = {
 
144
  "How-To Guide": [
145
  ("Intro Hook", "Problem Hook", 40, 60, "Hook the reader by presenting a common, relatable problem they're struggling with. Frame it as a challenge that many in their position face."),
146
  ("Intro Hook", "Promise the Outcome", 20, 30, "In one single, powerful sentence, promise the specific, tangible skill the reader will have after finishing this guide. Be extremely concise. Example: 'By the end of this guide, you will be able to build a high-performing global team from scratch.'"),
@@ -320,6 +268,7 @@ BLOG_STRUCTURES = {
320
  }
321
 
322
  # ======================== RESEARCH FUNCTIONS ========================
 
323
  def search_web(query): # This is now a fallback
324
  try:
325
  search_query = quote_plus(query)
@@ -776,7 +725,7 @@ def format_faq_content(text):
776
  answer = line[2:].strip()
777
  qa_pairs.append(f"Q. {current_q}\nA. {answer}")
778
  current_q = None # Reset to find the next question
779
-
780
  return "\n\n".join(qa_pairs)
781
 
782
 
@@ -906,17 +855,17 @@ class WordDocumentGenerator:
906
  for _, item_row in item_sections.iterrows():
907
  persona = next(self.ai_personas)
908
  yield f" -> Analyzing Item: '{item_row['Section Content'][:40]}...' (as {persona.split(',')[0]})"
909
-
910
  content = item_row['Section Content']
911
  # Extract the actual generated title from the first line of the content
912
  generated_title = content.split('\n')[0].strip() if content else 'Untitled Item'
913
-
914
  # Get the analysis for 'ideal_for' and 'business_benefit' from the AI
915
  details = self._extract_item_details(content, persona)
916
-
917
  # OVERRIDE the name with the actual generated title to ensure consistency
918
  details['name'] = generated_title
919
-
920
  listicle_items_data.append(details)
921
 
922
  toc_items = []
@@ -975,11 +924,11 @@ class WordDocumentGenerator:
975
  continue
976
 
977
  cleaned_content = clean_blog_content(section_content)
978
-
979
  # --- MODIFIED: Apply special FAQ formatting function ---
980
  if section_name == "FAQs":
981
  cleaned_content = format_faq_content(cleaned_content)
982
-
983
  if section_name == "At-a-Glance Matrix" and "Comparison" in content_type:
984
  header, data = parse_comparison_table(cleaned_content)
985
  if data: self.add_comparison_table(header, data); self.doc.add_paragraph(); continue
@@ -1044,6 +993,7 @@ def process_excel_to_word(excel_file_path):
1044
 
1045
  # ======================== GRADIO INTERFACE ========================
1046
  def create_interface():
 
1047
  generator = BlogGenerator()
1048
  with gr.Blocks(title="🎯 Digiworks Unified Blog Generator", theme=gr.themes.Soft()) as interface:
1049
  gr.HTML("""<div style='text-align: center; padding: 25px; background: linear-gradient(135deg, #1a365d 0%, #2d3748 100%); color: white; margin-bottom: 25px; border-radius: 15px;'><h1 style='margin: 0; font-size: 2.5em; font-weight: 900;'>🎯 Digiworks Unified Blog Generator</h1><p style='margin: 15px 0 0 0; font-size: 1.2em;'>Idea Generation β†’ Blog Post Creation β†’ Word Document Formatting</p></div>""")
@@ -1098,134 +1048,135 @@ def create_interface():
1098
  download_word_btn = gr.File(label="πŸ“₯ Download Formatted Word Document", visible=False)
1099
  gr.HTML("""<div style='background: #065f46; color: white; padding: 15px; border-radius: 8px; text-align: center; margin-left: 15px; border: 1px solid #10b981;'><strong style='color: #6ee7b7;'>πŸ’‘ WordPress Tip:</strong><span style='color: white;'>Copy sections directly from Word to WordPress editor!</span></div>""")
1100
 
1101
- # --- HANDLER FUNCTIONS (MODIFIED for Incremental Save) ---
1102
- def toggle_content_type(mode): return gr.update(visible=(mode == "Single Type"))
1103
- def handle_idea_generation(mode, content_type_val, num_topics_val):
1104
- try:
1105
- if mode == "Single Type": result, df, status = generate_blog_topics(content_type_val, num_topics_val)
1106
- else: result, df, status = generate_all_types(num_topics_val)
1107
- download_file, download_visible = None, False
1108
- if not df.empty:
1109
- timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
1110
- filename = f"digiworks_blog_ideas_{timestamp}.xlsx"
1111
- temp_path = os.path.join(tempfile.gettempdir(), filename)
1112
- with pd.ExcelWriter(temp_path, engine='openpyxl') as writer: df.to_excel(writer, sheet_name='Blog Ideas', index=False)
1113
- download_file, download_visible = temp_path, True
1114
- return status, result, df, gr.update(value=download_file, visible=download_visible)
1115
- except Exception as e: return f"❌ Error: {str(e)}", f"❌ Generation failed: {str(e)}", pd.DataFrame(), gr.update(visible=False)
1116
- def load_excel_and_create_checklist(file_path):
1117
- if not file_path: return gr.update(choices=[], value=[]), pd.DataFrame(), "Please upload an Excel file"
1118
- try:
1119
- df = pd.read_excel(file_path)
1120
- required_cols = ['Content Type', 'Headline']
1121
- if missing_cols := [col for col in required_cols if col not in df.columns]: return gr.update(choices=[], value=[]), pd.DataFrame(), f"Missing columns: {', '.join(missing_cols)}"
1122
- choices = []
1123
- for i, row in df.iterrows():
1124
- content_type = row.get('Content Type', '').replace(' πŸ“š', '').replace(' πŸ“‹', '').replace(' βš–οΈ', '').replace(' πŸ“ˆ', '').replace(' πŸ“Š', '').replace(' πŸ’‘', '')
1125
- headline = row.get('Headline', '')[:60]; keywords = row.get('Keywords', '')[:50]
1126
- choices.append(f"#{i+1} [{content_type}] {headline}... | Keywords: {keywords}...")
1127
- return gr.update(choices=choices, value=choices), df, f"βœ… Loaded {len(df)} topics. Select topics to generate blog posts."
1128
- except Exception as e: return gr.update(choices=[], value=[]), pd.DataFrame(), f"❌ Error: {str(e)}"
1129
- def reset_topic_counter():
1130
- generator.reset_counter()
1131
- return "πŸ”„ Topic counter reset to 0. Next generation will start from Topic #1."
1132
-
1133
- def generate_selected_posts(file_path, selected_choices, df):
1134
- if not file_path or not selected_choices:
1135
- yield "Please upload an Excel file and select at least one topic to generate.", pd.DataFrame(), gr.update(visible=False)
1136
- return
1137
- all_sections_data, status_log = [], ["πŸš€ Starting blog post generation..."]
1138
- latest_file_path = None
1139
- yield "\n".join(status_log), pd.DataFrame(), gr.update(value=None, visible=False)
1140
- try:
1141
- for topic_counter, choice in enumerate(selected_choices, 1):
1142
- choice_index = int(choice.split('#')[1].split(' ')[0]) - 1
1143
- if choice_index < len(df):
1144
- topic_data = df.iloc[choice_index].to_dict()
1145
- generation_iterator = generator.generate_blog_from_topic_data(topic_data, topic_index=topic_counter)
1146
-
1147
- topic_sections = []
1148
- for update_type, update_value, *rest in generation_iterator:
1149
- if update_type == 'status':
1150
- message = update_value
1151
- status_log.append(message)
1152
- yield "\n".join(status_log), pd.DataFrame(all_sections_data), gr.update(value=latest_file_path, visible=bool(latest_file_path))
1153
- elif update_type == 'result':
1154
- sections, msg = update_value, rest[0]
1155
- if sections:
1156
- topic_sections.extend(sections)
1157
- status_log.append(msg)
1158
-
1159
- if topic_sections:
1160
- all_sections_data.extend(topic_sections)
1161
- intermediate_df = pd.DataFrame(all_sections_data)
1162
- intermediate_file = create_excel_export(all_sections_data)
1163
- latest_file_path = intermediate_file
1164
- status_log.append(f"πŸ“¦ Intermediate file for Topic #{topic_counter} ready.")
1165
- yield "\n".join(status_log), intermediate_df, gr.update(value=latest_file_path, visible=True)
1166
-
1167
- if all_sections_data:
1168
- final_df = pd.DataFrame(all_sections_data)
1169
- total_topics, total_sections = len(selected_choices), len(all_sections_data)
1170
- successful_sections = len([s for s in all_sections_data if s['Word Count'] > 0])
1171
- total_words = sum(s['Word Count'] for s in all_sections_data if s['Word Count'] > 0)
1172
- final_status = f"""πŸŽ‰ ALL TOPICS COMPLETE!\n--------------------\nπŸ“Š Generated {total_sections} sections across {total_topics} topics\nβœ… Successful sections: {successful_sections}/{total_sections}\nπŸ“ Total words generated: {total_words:,}\nπŸ”’ Topics indexed: 1-{total_topics}\n\nFull Log:\n""" + "\n".join(status_log)
1173
- yield final_status, final_df, gr.update(value=latest_file_path, visible=True)
1174
- else:
1175
- yield "❌ No sections were generated.", pd.DataFrame(), gr.update(visible=False)
1176
- except Exception as e:
1177
- import traceback
1178
- tb_str = traceback.format_exc()
1179
- yield f"❌ An unexpected error occurred: {str(e)}\n\nTraceback:\n{tb_str}", pd.DataFrame(), gr.update(visible=False)
1180
-
1181
- def process_excel_file(excel_file):
1182
- if not excel_file: return "Please upload an Excel file", "", gr.update(visible=False)
1183
- try:
1184
- df = pd.read_excel(excel_file); preview = "No content preview available"
1185
- if not df.empty and 'Section Content' in df.columns:
1186
- cleaned_sample = clean_blog_content(df.iloc[0]['Section Content'])
1187
- preview = cleaned_sample[:500] + "..." if len(cleaned_sample) > 500 else cleaned_sample
1188
- status = f"βœ… Excel loaded: {len(df)} sections found\nπŸ“Š Topics: {df['Topic Number'].nunique() if 'Topic Number' in df.columns else 'Unknown'}\nπŸ”„ Ready to generate Word document"
1189
- return status, preview, gr.update(visible=False)
1190
- except Exception as e: return f"❌ Error reading Excel: {str(e)}", "", gr.update(visible=False)
1191
-
1192
- def generate_word_document(excel_file):
1193
- if not excel_file:
1194
- yield "Please upload an Excel file.", "", gr.update(visible=False)
1195
- return
1196
- status_log = ["πŸš€ Starting Word document generation..."]
1197
- yield "\n".join(status_log), "", gr.update(visible=False)
1198
- try:
1199
- processing_generator = process_excel_to_word(excel_file)
1200
- for type, val1, *val2 in processing_generator:
1201
- if type == 'status':
1202
- status_log.append(val1)
1203
- yield "\n".join(status_log), "", gr.update(visible=False)
1204
- elif type == 'complete':
1205
- word_file_path, result_message = val1, val2[0]
1206
- df = pd.read_excel(excel_file)
1207
- preview = "Document generated successfully"
1208
- if not df.empty and 'Section Content' in df.columns:
1209
- cleaned_sample = clean_blog_content(df.iloc[0]['Section Content'])
1210
- preview = cleaned_sample[:500] + "..." if len(cleaned_sample) > 500 else cleaned_sample
1211
- yield result_message, preview, gr.update(value=word_file_path, visible=True)
1212
- elif type == 'error':
1213
- yield val1, "", gr.update(visible=False)
1214
- except Exception as e:
1215
- import traceback
1216
- yield f"❌ An unexpected error occurred: {str(e)}\n{traceback.format_exc()}", "", gr.update(visible=False)
1217
-
1218
- # --- EVENT LISTENERS ---
1219
- mode_choice.change(fn=toggle_content_type, inputs=[mode_choice], outputs=[content_type])
1220
- generate_ideas_btn.click(fn=handle_idea_generation, inputs=[mode_choice, content_type, num_topics], outputs=[ideas_status, ideas_results, ideas_df, download_ideas_btn])
1221
- excel_upload.change(fn=load_excel_and_create_checklist, inputs=[excel_upload], outputs=[topics_checklist, loaded_data, posts_status])
1222
- reset_counter_btn.click(fn=reset_topic_counter, inputs=[], outputs=[posts_status])
1223
- generate_posts_btn.click(fn=generate_selected_posts, inputs=[excel_upload, topics_checklist, loaded_data], outputs=[posts_status, posts_dataframe, download_posts_btn])
1224
- word_excel_input.change(fn=process_excel_file, inputs=[word_excel_input], outputs=[word_status, word_preview, download_word_btn])
1225
- generate_word_btn.click(fn=generate_word_document, inputs=[word_excel_input], outputs=[word_status, word_preview, download_word_btn])
1226
- gr.HTML("""<div style='text-align: center; padding: 20px; margin-top: 25px; border-top: 1px solid #e0e0e0;'><p style='color: #666; margin: 0;'>🎯 <strong>Digiworks Unified Blog Generator</strong> | Idea Generation β†’ Blog Post Creation β†’ Word Document Formatting<br><small>Perfect for content teams and marketers</small></p></div>""")
1227
-
1228
- return interface
 
1229
 
1230
  # ======================== MAIN EXECUTION ========================
1231
  if __name__ == "__main__":
@@ -1239,7 +1190,6 @@ if __name__ == "__main__":
1239
  print("πŸ”„ Optimized comparison tables for 'At-a-Glance Matrix' sections")
1240
  print("="*70)
1241
  interface = create_interface()
1242
- # MODIFIED: Add authentication with a username and password tuple
1243
- interface.launch(auth=("your_username", "your_password"), share=True, debug=True)
1244
- print("βœ… Unified blog generator launched!")
1245
-
 
1
+ # MODIFIED: Removed all runtime installation code. Dependencies are now in requirements.txt
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  import subprocess
3
  import sys
4
+ import os
5
  import re
6
  import time
7
  import json
 
21
  from docx.oxml.ns import qn
22
  from itertools import cycle
23
  from serpapi import GoogleSearch
24
+ import spacy # MODIFIED: Directly import spacy
25
+
26
+ # MODIFIED: Load the spacy model directly, as it's installed via requirements.txt
27
+ nlp = spacy.load("en_core_web_sm")
28
 
29
  # ======================== CONFIGURE OPENAI & SERPAPI ========================
30
  # MODIFIED: Get API keys from Hugging Face Secrets and add error handling
 
40
  client = OpenAI(api_key=openai_api_key)
41
  SERPAPI_KEY = serpapi_key
42
 
 
43
  # ======================== BLOG CONTENT TYPES ========================
44
  BLOG_TYPES = {
45
  "How-To Guide πŸ“š": "step-by-step tutorials",
 
88
  """
89
 
90
  BLOG_STRUCTURES = {
91
+ # ... (Your entire BLOG_STRUCTURES dictionary remains here, unchanged)
92
  "How-To Guide": [
93
  ("Intro Hook", "Problem Hook", 40, 60, "Hook the reader by presenting a common, relatable problem they're struggling with. Frame it as a challenge that many in their position face."),
94
  ("Intro Hook", "Promise the Outcome", 20, 30, "In one single, powerful sentence, promise the specific, tangible skill the reader will have after finishing this guide. Be extremely concise. Example: 'By the end of this guide, you will be able to build a high-performing global team from scratch.'"),
 
268
  }
269
 
270
  # ======================== RESEARCH FUNCTIONS ========================
271
+ # ... (All your functions from search_web down to the line before WordDocumentGenerator remain here, unchanged)
272
  def search_web(query): # This is now a fallback
273
  try:
274
  search_query = quote_plus(query)
 
725
  answer = line[2:].strip()
726
  qa_pairs.append(f"Q. {current_q}\nA. {answer}")
727
  current_q = None # Reset to find the next question
728
+
729
  return "\n\n".join(qa_pairs)
730
 
731
 
 
855
  for _, item_row in item_sections.iterrows():
856
  persona = next(self.ai_personas)
857
  yield f" -> Analyzing Item: '{item_row['Section Content'][:40]}...' (as {persona.split(',')[0]})"
858
+
859
  content = item_row['Section Content']
860
  # Extract the actual generated title from the first line of the content
861
  generated_title = content.split('\n')[0].strip() if content else 'Untitled Item'
862
+
863
  # Get the analysis for 'ideal_for' and 'business_benefit' from the AI
864
  details = self._extract_item_details(content, persona)
865
+
866
  # OVERRIDE the name with the actual generated title to ensure consistency
867
  details['name'] = generated_title
868
+
869
  listicle_items_data.append(details)
870
 
871
  toc_items = []
 
924
  continue
925
 
926
  cleaned_content = clean_blog_content(section_content)
927
+
928
  # --- MODIFIED: Apply special FAQ formatting function ---
929
  if section_name == "FAQs":
930
  cleaned_content = format_faq_content(cleaned_content)
931
+
932
  if section_name == "At-a-Glance Matrix" and "Comparison" in content_type:
933
  header, data = parse_comparison_table(cleaned_content)
934
  if data: self.add_comparison_table(header, data); self.doc.add_paragraph(); continue
 
993
 
994
  # ======================== GRADIO INTERFACE ========================
995
  def create_interface():
996
+ # ... (Your entire Gradio interface layout remains here, unchanged)
997
  generator = BlogGenerator()
998
  with gr.Blocks(title="🎯 Digiworks Unified Blog Generator", theme=gr.themes.Soft()) as interface:
999
  gr.HTML("""<div style='text-align: center; padding: 25px; background: linear-gradient(135deg, #1a365d 0%, #2d3748 100%); color: white; margin-bottom: 25px; border-radius: 15px;'><h1 style='margin: 0; font-size: 2.5em; font-weight: 900;'>🎯 Digiworks Unified Blog Generator</h1><p style='margin: 15px 0 0 0; font-size: 1.2em;'>Idea Generation β†’ Blog Post Creation β†’ Word Document Formatting</p></div>""")
 
1048
  download_word_btn = gr.File(label="πŸ“₯ Download Formatted Word Document", visible=False)
1049
  gr.HTML("""<div style='background: #065f46; color: white; padding: 15px; border-radius: 8px; text-align: center; margin-left: 15px; border: 1px solid #10b981;'><strong style='color: #6ee7b7;'>πŸ’‘ WordPress Tip:</strong><span style='color: white;'>Copy sections directly from Word to WordPress editor!</span></div>""")
1050
 
1051
+ # --- HANDLER FUNCTIONS (MODIFIED for Incremental Save) ---
1052
+ def toggle_content_type(mode): return gr.update(visible=(mode == "Single Type"))
1053
+ def handle_idea_generation(mode, content_type_val, num_topics_val):
1054
+ try:
1055
+ if mode == "Single Type": result, df, status = generate_blog_topics(content_type_val, num_topics_val)
1056
+ else: result, df, status = generate_all_types(num_topics_val)
1057
+ download_file, download_visible = None, False
1058
+ if not df.empty:
1059
+ timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
1060
+ filename = f"digiworks_blog_ideas_{timestamp}.xlsx"
1061
+ temp_path = os.path.join(tempfile.gettempdir(), filename)
1062
+ with pd.ExcelWriter(temp_path, engine='openpyxl') as writer: df.to_excel(writer, sheet_name='Blog Ideas', index=False)
1063
+ download_file, download_visible = temp_path, True
1064
+ return status, result, df, gr.update(value=download_file, visible=download_visible)
1065
+ except Exception as e: return f"❌ Error: {str(e)}", f"❌ Generation failed: {str(e)}", pd.DataFrame(), gr.update(visible=False)
1066
+ def load_excel_and_create_checklist(file_path):
1067
+ if not file_path: return gr.update(choices=[], value=[]), pd.DataFrame(), "Please upload an Excel file"
1068
+ try:
1069
+ df = pd.read_excel(file_path)
1070
+ required_cols = ['Content Type', 'Headline']
1071
+ if missing_cols := [col for col in required_cols if col not in df.columns]: return gr.update(choices=[], value=[]), pd.DataFrame(), f"Missing columns: {', '.join(missing_cols)}"
1072
+ choices = []
1073
+ for i, row in df.iterrows():
1074
+ content_type = row.get('Content Type', '').replace(' πŸ“š', '').replace(' πŸ“‹', '').replace(' βš–οΈ', '').replace(' πŸ“ˆ', '').replace(' πŸ“Š', '').replace(' πŸ’‘', '')
1075
+ headline = row.get('Headline', '')[:60]; keywords = row.get('Keywords', '')[:50]
1076
+ choices.append(f"#{i+1} [{content_type}] {headline}... | Keywords: {keywords}...")
1077
+ return gr.update(choices=choices, value=choices), df, f"βœ… Loaded {len(df)} topics. Select topics to generate blog posts."
1078
+ except Exception as e: return gr.update(choices=[], value=[]), pd.DataFrame(), f"❌ Error: {str(e)}"
1079
+ def reset_topic_counter():
1080
+ generator.reset_counter()
1081
+ return "πŸ”„ Topic counter reset to 0. Next generation will start from Topic #1."
1082
+
1083
+ def generate_selected_posts(file_path, selected_choices, df):
1084
+ if not file_path or not selected_choices:
1085
+ yield "Please upload an Excel file and select at least one topic to generate.", pd.DataFrame(), gr.update(visible=False)
1086
+ return
1087
+ all_sections_data, status_log = [], ["πŸš€ Starting blog post generation..."]
1088
+ latest_file_path = None
1089
+ yield "\n".join(status_log), pd.DataFrame(), gr.update(value=None, visible=False)
1090
+ try:
1091
+ for topic_counter, choice in enumerate(selected_choices, 1):
1092
+ choice_index = int(choice.split('#')[1].split(' ')[0]) - 1
1093
+ if choice_index < len(df):
1094
+ topic_data = df.iloc[choice_index].to_dict()
1095
+ generation_iterator = generator.generate_blog_from_topic_data(topic_data, topic_index=topic_counter)
1096
+
1097
+ topic_sections = []
1098
+ for update_type, update_value, *rest in generation_iterator:
1099
+ if update_type == 'status':
1100
+ message = update_value
1101
+ status_log.append(message)
1102
+ yield "\n".join(status_log), pd.DataFrame(all_sections_data), gr.update(value=latest_file_path, visible=bool(latest_file_path))
1103
+ elif update_type == 'result':
1104
+ sections, msg = update_value, rest[0]
1105
+ if sections:
1106
+ topic_sections.extend(sections)
1107
+ status_log.append(msg)
1108
+
1109
+ if topic_sections:
1110
+ all_sections_data.extend(topic_sections)
1111
+ intermediate_df = pd.DataFrame(all_sections_data)
1112
+ intermediate_file = create_excel_export(all_sections_data)
1113
+ latest_file_path = intermediate_file
1114
+ status_log.append(f"πŸ“¦ Intermediate file for Topic #{topic_counter} ready.")
1115
+ yield "\n".join(status_log), intermediate_df, gr.update(value=latest_file_path, visible=True)
1116
+
1117
+ if all_sections_data:
1118
+ final_df = pd.DataFrame(all_sections_data)
1119
+ total_topics, total_sections = len(selected_choices), len(all_sections_data)
1120
+ successful_sections = len([s for s in all_sections_data if s['Word Count'] > 0])
1121
+ total_words = sum(s['Word Count'] for s in all_sections_data if s['Word Count'] > 0)
1122
+ final_status = f"""πŸŽ‰ ALL TOPICS COMPLETE!\n--------------------\nπŸ“Š Generated {total_sections} sections across {total_topics} topics\nβœ… Successful sections: {successful_sections}/{total_sections}\nπŸ“ Total words generated: {total_words:,}\nπŸ”’ Topics indexed: 1-{total_topics}\n\nFull Log:\n""" + "\n".join(status_log)
1123
+ yield final_status, final_df, gr.update(value=latest_file_path, visible=True)
1124
+ else:
1125
+ yield "❌ No sections were generated.", pd.DataFrame(), gr.update(visible=False)
1126
+ except Exception as e:
1127
+ import traceback
1128
+ tb_str = traceback.format_exc()
1129
+ yield f"❌ An unexpected error occurred: {str(e)}\n\nTraceback:\n{tb_str}", pd.DataFrame(), gr.update(visible=False)
1130
+
1131
+ def process_excel_file(excel_file):
1132
+ if not excel_file: return "Please upload an Excel file", "", gr.update(visible=False)
1133
+ try:
1134
+ df = pd.read_excel(excel_file); preview = "No content preview available"
1135
+ if not df.empty and 'Section Content' in df.columns:
1136
+ cleaned_sample = clean_blog_content(df.iloc[0]['Section Content'])
1137
+ preview = cleaned_sample[:500] + "..." if len(cleaned_sample) > 500 else cleaned_sample
1138
+ status = f"βœ… Excel loaded: {len(df)} sections found\nπŸ“Š Topics: {df['Topic Number'].nunique() if 'Topic Number' in df.columns else 'Unknown'}\nπŸ”„ Ready to generate Word document"
1139
+ return status, preview, gr.update(visible=False)
1140
+ except Exception as e: return f"❌ Error reading Excel: {str(e)}", "", gr.update(visible=False)
1141
+
1142
+ def generate_word_document(excel_file):
1143
+ if not excel_file:
1144
+ yield "Please upload an Excel file.", "", gr.update(visible=False)
1145
+ return
1146
+ status_log = ["πŸš€ Starting Word document generation..."]
1147
+ yield "\n".join(status_log), "", gr.update(visible=False)
1148
+ try:
1149
+ processing_generator = process_excel_to_word(excel_file)
1150
+ for type, val1, *val2 in processing_generator:
1151
+ if type == 'status':
1152
+ status_log.append(val1)
1153
+ yield "\n".join(status_log), "", gr.update(visible=False)
1154
+ elif type == 'complete':
1155
+ word_file_path, result_message = val1, val2[0]
1156
+ df = pd.read_excel(excel_file)
1157
+ preview = "Document generated successfully"
1158
+ if not df.empty and 'Section Content' in df.columns:
1159
+ cleaned_sample = clean_blog_content(df.iloc[0]['Section Content'])
1160
+ preview = cleaned_sample[:500] + "..." if len(cleaned_sample) > 500 else cleaned_sample
1161
+ yield result_message, preview, gr.update(value=word_file_path, visible=True)
1162
+ elif type == 'error':
1163
+ yield val1, "", gr.update(visible=False)
1164
+ except Exception as e:
1165
+ import traceback
1166
+ yield f"❌ An unexpected error occurred: {str(e)}\n{traceback.format_exc()}", "", gr.update(visible=False)
1167
+
1168
+ # --- EVENT LISTENERS ---
1169
+ mode_choice.change(fn=toggle_content_type, inputs=[mode_choice], outputs=[content_type])
1170
+ generate_ideas_btn.click(fn=handle_idea_generation, inputs=[mode_choice, content_type, num_topics], outputs=[ideas_status, ideas_results, ideas_df, download_ideas_btn])
1171
+ excel_upload.change(fn=load_excel_and_create_checklist, inputs=[excel_upload], outputs=[topics_checklist, loaded_data, posts_status])
1172
+ reset_counter_btn.click(fn=reset_topic_counter, inputs=[], outputs=[posts_status])
1173
+ generate_posts_btn.click(fn=generate_selected_posts, inputs=[excel_upload, topics_checklist, loaded_data], outputs=[posts_status, posts_dataframe, download_posts_btn])
1174
+ word_excel_input.change(fn=process_excel_file, inputs=[word_excel_input], outputs=[word_status, word_preview, download_word_btn])
1175
+ generate_word_btn.click(fn=generate_word_document, inputs=[word_excel_input], outputs=[word_status, word_preview, download_word_btn])
1176
+ gr.HTML("""<div style='text-align: center; padding: 20px; margin-top: 25px; border-top: 1px solid #e0e0e0;'><p style='color: #666; margin: 0;'>🎯 <strong>Digiworks Unified Blog Generator</strong> | Idea Generation β†’ Blog Post Creation β†’ Word Document Formatting<br><small>Perfect for content teams and marketers</small></p></div>""")
1177
+
1178
+ return interface
1179
+
1180
 
1181
  # ======================== MAIN EXECUTION ========================
1182
  if __name__ == "__main__":
 
1190
  print("πŸ”„ Optimized comparison tables for 'At-a-Glance Matrix' sections")
1191
  print("="*70)
1192
  interface = create_interface()
1193
+ # MODIFIED: Launch call is simplified for Hugging Face deployment
1194
+ interface.launch(auth=("your_username", "your_password"))
1195
+ print("βœ… Unified blog generator launched!")