Blog SEO Analysis Modal - Updated with SEO Metadata Generator, Core Metadata Tab, and Metadata Display Components

This commit is contained in:
ajaysi
2025-09-23 16:21:09 +05:30
parent 12119d418b
commit a91677782e
16 changed files with 3433 additions and 89 deletions

View File

@@ -21,6 +21,7 @@ router = APIRouter(prefix="/api/blog-writer/seo", tags=["Blog SEO Analysis"])
class SEOAnalysisRequest(BaseModel):
"""Request model for SEO analysis"""
blog_content: str
blog_title: Optional[str] = None
research_data: Dict[str, Any]
user_id: Optional[str] = None
session_id: Optional[str] = None
@@ -34,6 +35,8 @@ class SEOAnalysisResponse(BaseModel):
category_scores: Dict[str, float]
analysis_summary: Dict[str, Any]
actionable_recommendations: list
detailed_analysis: Optional[Dict[str, Any]] = None
visualization_data: Optional[Dict[str, Any]] = None
generated_at: str
error: Optional[str] = None
@@ -87,7 +90,8 @@ async def analyze_blog_seo(request: SEOAnalysisRequest):
# Perform SEO analysis
analysis_results = await seo_analyzer.analyze_blog_content(
blog_content=request.blog_content,
research_data=request.research_data
research_data=request.research_data,
blog_title=request.blog_title
)
# Check for errors
@@ -100,6 +104,8 @@ async def analyze_blog_seo(request: SEOAnalysisRequest):
category_scores={},
analysis_summary={},
actionable_recommendations=[],
detailed_analysis=None,
visualization_data=None,
generated_at=analysis_results.get('generated_at', ''),
error=analysis_results['error']
)
@@ -112,6 +118,8 @@ async def analyze_blog_seo(request: SEOAnalysisRequest):
category_scores=analysis_results.get('category_scores', {}),
analysis_summary=analysis_results.get('analysis_summary', {}),
actionable_recommendations=analysis_results.get('actionable_recommendations', []),
detailed_analysis=analysis_results.get('detailed_analysis'),
visualization_data=analysis_results.get('visualization_data'),
generated_at=analysis_results.get('generated_at', '')
)

View File

@@ -162,6 +162,7 @@ class BlogOptimizeResponse(BaseModel):
class BlogSEOAnalyzeRequest(BaseModel):
content: str
blog_title: Optional[str] = None
keywords: List[str] = []
research_data: Optional[Dict[str, Any]] = None
@@ -181,15 +182,28 @@ class BlogSEOMetadataRequest(BaseModel):
content: str
title: Optional[str] = None
keywords: List[str] = []
research_data: Optional[Dict[str, Any]] = None
class BlogSEOMetadataResponse(BaseModel):
success: bool = True
title_options: List[str]
meta_descriptions: List[str]
open_graph: Dict[str, Any]
twitter_card: Dict[str, Any]
schema_data: Dict[str, Any]
title_options: List[str] = []
meta_descriptions: List[str] = []
seo_title: Optional[str] = None
meta_description: Optional[str] = None
url_slug: Optional[str] = None
blog_tags: List[str] = []
blog_categories: List[str] = []
social_hashtags: List[str] = []
open_graph: Dict[str, Any] = {}
twitter_card: Dict[str, Any] = {}
json_ld_schema: Dict[str, Any] = {}
canonical_url: Optional[str] = None
reading_time: float = 0.0
focus_keyword: Optional[str] = None
generated_at: Optional[str] = None
optimization_score: int = 0
error: Optional[str] = None
class BlogPublishRequest(BaseModel):

View File

@@ -268,16 +268,53 @@ class BlogWriterService:
)
async def seo_metadata(self, request: BlogSEOMetadataRequest) -> BlogSEOMetadataResponse:
"""Generate SEO metadata for content."""
# TODO: Move to optimization module
return BlogSEOMetadataResponse(
success=True,
title_options=[request.title or "Generated SEO Title"],
meta_descriptions=["Compelling meta description..."],
open_graph={"title": request.title or "OG Title", "image": ""},
twitter_card={"card": "summary_large_image"},
schema={"@type": "Article"},
)
"""Generate comprehensive SEO metadata for content."""
try:
from services.blog_writer.seo.blog_seo_metadata_generator import BlogSEOMetadataGenerator
# Initialize metadata generator
metadata_generator = BlogSEOMetadataGenerator()
# Generate comprehensive metadata
metadata_results = await metadata_generator.generate_comprehensive_metadata(
blog_content=request.content,
blog_title=request.title or "Untitled Blog Post",
research_data=request.research_data or {}
)
# Convert to BlogSEOMetadataResponse format
return BlogSEOMetadataResponse(
success=metadata_results.get('success', True),
title_options=metadata_results.get('title_options', []),
meta_descriptions=metadata_results.get('meta_descriptions', []),
seo_title=metadata_results.get('seo_title'),
meta_description=metadata_results.get('meta_description'),
url_slug=metadata_results.get('url_slug', ''),
blog_tags=metadata_results.get('blog_tags', []),
blog_categories=metadata_results.get('blog_categories', []),
social_hashtags=metadata_results.get('social_hashtags', []),
open_graph=metadata_results.get('open_graph', {}),
twitter_card=metadata_results.get('twitter_card', {}),
json_ld_schema=metadata_results.get('json_ld_schema', {}),
canonical_url=metadata_results.get('canonical_url', ''),
reading_time=metadata_results.get('reading_time', 0.0),
focus_keyword=metadata_results.get('focus_keyword', ''),
generated_at=metadata_results.get('generated_at', ''),
optimization_score=metadata_results.get('metadata_summary', {}).get('optimization_score', 0)
)
except Exception as e:
logger.error(f"SEO metadata generation failed: {e}")
# Return fallback response
return BlogSEOMetadataResponse(
success=False,
title_options=[request.title or "Generated SEO Title"],
meta_descriptions=["Compelling meta description..."],
open_graph={"title": request.title or "OG Title", "image": ""},
twitter_card={"card": "summary_large_image"},
json_ld_schema={"@type": "Article"},
error=str(e)
)
async def publish(self, request: BlogPublishRequest) -> BlogPublishResponse:
"""Publish content to specified platform."""

View File

@@ -32,7 +32,7 @@ class BlogContentSEOAnalyzer:
logger.info("BlogContentSEOAnalyzer initialized")
async def analyze_blog_content(self, blog_content: str, research_data: Dict[str, Any]) -> Dict[str, Any]:
async def analyze_blog_content(self, blog_content: str, research_data: Dict[str, Any], blog_title: Optional[str] = None) -> Dict[str, Any]:
"""
Main analysis method with parallel processing

View File

@@ -0,0 +1,488 @@
"""
Blog SEO Metadata Generator
Optimized SEO metadata generation service that uses maximum 2 AI calls
to generate comprehensive metadata including titles, descriptions,
Open Graph tags, Twitter cards, and structured data.
"""
import asyncio
import re
from datetime import datetime
from typing import Dict, Any, List, Optional
from loguru import logger
from services.llm_providers.gemini_provider import gemini_structured_json_response
class BlogSEOMetadataGenerator:
"""Optimized SEO metadata generator with maximum 2 AI calls"""
def __init__(self):
"""Initialize the metadata generator"""
self.gemini_provider = gemini_structured_json_response
logger.info("BlogSEOMetadataGenerator initialized")
async def generate_comprehensive_metadata(
self,
blog_content: str,
blog_title: str,
research_data: Dict[str, Any]
) -> Dict[str, Any]:
"""
Generate comprehensive SEO metadata using maximum 2 AI calls
Args:
blog_content: The blog content to analyze
blog_title: The blog title
research_data: Research data containing keywords and insights
Returns:
Comprehensive metadata including all SEO elements
"""
try:
logger.info("Starting comprehensive SEO metadata generation")
# Extract keywords and context from research data
keywords_data = self._extract_keywords_from_research(research_data)
logger.info(f"Extracted keywords: {keywords_data}")
# Call 1: Generate core SEO metadata (parallel with Call 2)
logger.info("Generating core SEO metadata")
core_metadata_task = self._generate_core_metadata(blog_content, blog_title, keywords_data)
# Call 2: Generate social media and structured data (parallel with Call 1)
logger.info("Generating social media and structured data")
social_metadata_task = self._generate_social_metadata(blog_content, blog_title, keywords_data)
# Wait for both calls to complete
core_metadata, social_metadata = await asyncio.gather(
core_metadata_task,
social_metadata_task
)
# Compile final response
results = self._compile_metadata_response(core_metadata, social_metadata, blog_title)
logger.info(f"SEO metadata generation completed successfully")
return results
except Exception as e:
logger.error(f"SEO metadata generation failed: {e}")
# Fail fast - don't return fallback data
raise e
def _extract_keywords_from_research(self, research_data: Dict[str, Any]) -> Dict[str, Any]:
"""Extract keywords and context from research data"""
try:
keyword_analysis = research_data.get('keyword_analysis', {})
# Handle both 'semantic' and 'semantic_keywords' field names
semantic_keywords = keyword_analysis.get('semantic', []) or keyword_analysis.get('semantic_keywords', [])
return {
'primary_keywords': keyword_analysis.get('primary', []),
'long_tail_keywords': keyword_analysis.get('long_tail', []),
'semantic_keywords': semantic_keywords,
'all_keywords': keyword_analysis.get('all_keywords', []),
'search_intent': keyword_analysis.get('search_intent', 'informational'),
'target_audience': research_data.get('target_audience', 'general'),
'industry': research_data.get('industry', 'general')
}
except Exception as e:
logger.error(f"Failed to extract keywords from research: {e}")
return {
'primary_keywords': [],
'long_tail_keywords': [],
'semantic_keywords': [],
'all_keywords': [],
'search_intent': 'informational',
'target_audience': 'general',
'industry': 'general'
}
async def _generate_core_metadata(
self,
blog_content: str,
blog_title: str,
keywords_data: Dict[str, Any]
) -> Dict[str, Any]:
"""Generate core SEO metadata (Call 1)"""
try:
# Create comprehensive prompt for core metadata
prompt = self._create_core_metadata_prompt(blog_content, blog_title, keywords_data)
# Define simplified structured schema for core metadata
schema = {
"type": "object",
"properties": {
"seo_title": {
"type": "string",
"description": "SEO-optimized title (50-60 characters)"
},
"meta_description": {
"type": "string",
"description": "Meta description (150-160 characters)"
},
"url_slug": {
"type": "string",
"description": "URL slug (lowercase, hyphens)"
},
"blog_tags": {
"type": "array",
"items": {"type": "string"},
"description": "Blog tags array"
},
"blog_categories": {
"type": "array",
"items": {"type": "string"},
"description": "Blog categories array"
},
"social_hashtags": {
"type": "array",
"items": {"type": "string"},
"description": "Social media hashtags array"
},
"reading_time": {
"type": "integer",
"description": "Reading time in minutes"
},
"focus_keyword": {
"type": "string",
"description": "Primary focus keyword"
}
},
"required": ["seo_title", "meta_description", "url_slug", "blog_tags", "blog_categories", "social_hashtags", "reading_time", "focus_keyword"]
}
# Get structured response from Gemini
ai_response = self.gemini_provider(
prompt=prompt,
schema=schema,
temperature=0.3,
max_tokens=2048
)
# Check if we got a valid response
if not ai_response or not isinstance(ai_response, dict):
logger.error("Core metadata generation failed: Invalid response from Gemini")
# Return fallback response
return {
'seo_title': blog_title,
'meta_description': f'Learn about {primary_keywords.split(", ")[0] if primary_keywords else "this topic"}.',
'url_slug': blog_title.lower().replace(' ', '-').replace(':', '').replace(',', '')[:50],
'blog_tags': primary_keywords.split(', ') if primary_keywords else ['content'],
'blog_categories': ['Content Marketing', 'Technology'],
'social_hashtags': ['#content', '#marketing', '#technology'],
'reading_time': max(1, word_count // 200),
'focus_keyword': primary_keywords.split(', ')[0] if primary_keywords else 'content'
}
logger.info(f"Core metadata generation completed. Response keys: {list(ai_response.keys())}")
logger.info(f"Core metadata response: {ai_response}")
return ai_response
except Exception as e:
logger.error(f"Core metadata generation failed: {e}")
raise e
async def _generate_social_metadata(
self,
blog_content: str,
blog_title: str,
keywords_data: Dict[str, Any]
) -> Dict[str, Any]:
"""Generate social media and structured data (Call 2)"""
try:
# Create comprehensive prompt for social metadata
prompt = self._create_social_metadata_prompt(blog_content, blog_title, keywords_data)
# Define simplified structured schema for social metadata
schema = {
"type": "object",
"properties": {
"open_graph": {
"type": "object",
"properties": {
"title": {"type": "string"},
"description": {"type": "string"},
"image": {"type": "string"},
"type": {"type": "string"},
"site_name": {"type": "string"},
"url": {"type": "string"}
}
},
"twitter_card": {
"type": "object",
"properties": {
"card": {"type": "string"},
"title": {"type": "string"},
"description": {"type": "string"},
"image": {"type": "string"},
"site": {"type": "string"},
"creator": {"type": "string"}
}
},
"json_ld_schema": {
"type": "object",
"properties": {
"@context": {"type": "string"},
"@type": {"type": "string"},
"headline": {"type": "string"},
"description": {"type": "string"},
"author": {"type": "object"},
"publisher": {"type": "object"},
"datePublished": {"type": "string"},
"dateModified": {"type": "string"},
"mainEntityOfPage": {"type": "string"},
"keywords": {"type": "array"},
"wordCount": {"type": "integer"}
}
}
},
"required": ["open_graph", "twitter_card", "json_ld_schema"]
}
# Get structured response from Gemini
ai_response = self.gemini_provider(
prompt=prompt,
schema=schema,
temperature=0.3,
max_tokens=2048
)
# Check if we got a valid response
if not ai_response or not isinstance(ai_response, dict) or not ai_response.get('open_graph') or not ai_response.get('twitter_card') or not ai_response.get('json_ld_schema'):
logger.error("Social metadata generation failed: Invalid or empty response from Gemini")
# Return fallback response
return {
'open_graph': {
'title': blog_title,
'description': f'Learn about {keywords_data.get("primary_keywords", ["this topic"])[0] if keywords_data.get("primary_keywords") else "this topic"}.',
'image': 'https://example.com/image.jpg',
'type': 'article',
'site_name': 'Your Website',
'url': 'https://example.com/blog'
},
'twitter_card': {
'card': 'summary_large_image',
'title': blog_title,
'description': f'Learn about {keywords_data.get("primary_keywords", ["this topic"])[0] if keywords_data.get("primary_keywords") else "this topic"}.',
'image': 'https://example.com/image.jpg',
'site': '@yourwebsite',
'creator': '@author'
},
'json_ld_schema': {
'@context': 'https://schema.org',
'@type': 'Article',
'headline': blog_title,
'description': f'Learn about {keywords_data.get("primary_keywords", ["this topic"])[0] if keywords_data.get("primary_keywords") else "this topic"}.',
'author': {'@type': 'Person', 'name': 'Author Name'},
'publisher': {'@type': 'Organization', 'name': 'Your Website'},
'datePublished': '2025-01-01T00:00:00Z',
'dateModified': '2025-01-01T00:00:00Z',
'mainEntityOfPage': 'https://example.com/blog',
'keywords': keywords_data.get('primary_keywords', ['content']),
'wordCount': len(blog_content.split())
}
}
logger.info(f"Social metadata generation completed. Response keys: {list(ai_response.keys())}")
logger.info(f"Open Graph data: {ai_response.get('open_graph', 'Not found')}")
logger.info(f"Twitter Card data: {ai_response.get('twitter_card', 'Not found')}")
logger.info(f"JSON-LD data: {ai_response.get('json_ld_schema', 'Not found')}")
return ai_response
except Exception as e:
logger.error(f"Social metadata generation failed: {e}")
raise e
def _create_core_metadata_prompt(
self,
blog_content: str,
blog_title: str,
keywords_data: Dict[str, Any]
) -> str:
"""Create high-quality prompt for core metadata generation"""
primary_keywords = ", ".join(keywords_data.get('primary_keywords', []))
semantic_keywords = ", ".join(keywords_data.get('semantic_keywords', []))
search_intent = keywords_data.get('search_intent', 'informational')
target_audience = keywords_data.get('target_audience', 'general')
industry = keywords_data.get('industry', 'general')
# Calculate word count for reading time estimation
word_count = len(blog_content.split())
prompt = f"""
Generate SEO metadata for this blog post.
BLOG TITLE: {blog_title}
BLOG CONTENT: {blog_content[:1000]}...
PRIMARY KEYWORDS: {primary_keywords}
SEMANTIC KEYWORDS: {semantic_keywords}
WORD COUNT: {word_count}
Generate:
1. SEO TITLE (50-60 characters) - include primary keyword
2. META DESCRIPTION (150-160 characters) - include CTA
3. URL SLUG (lowercase, hyphens, 3-5 words)
4. BLOG TAGS (5-8 relevant tags)
5. BLOG CATEGORIES (2-3 categories)
6. SOCIAL HASHTAGS (5-10 hashtags with #)
7. READING TIME (calculate from {word_count} words)
8. FOCUS KEYWORD (primary keyword for SEO)
Make it compelling and SEO-optimized.
"""
return prompt
def _create_social_metadata_prompt(
self,
blog_content: str,
blog_title: str,
keywords_data: Dict[str, Any]
) -> str:
"""Create high-quality prompt for social metadata generation"""
primary_keywords = ", ".join(keywords_data.get('primary_keywords', []))
search_intent = keywords_data.get('search_intent', 'informational')
target_audience = keywords_data.get('target_audience', 'general')
industry = keywords_data.get('industry', 'general')
current_date = datetime.now().isoformat()
prompt = f"""
Generate social media metadata for this blog post.
BLOG TITLE: {blog_title}
BLOG CONTENT: {blog_content[:800]}...
PRIMARY KEYWORDS: {primary_keywords}
CURRENT DATE: {current_date}
Generate:
1. OPEN GRAPH (Facebook/LinkedIn):
- title: 60 chars max
- description: 160 chars max
- image: image URL
- type: "article"
- site_name: site name
- url: canonical URL
2. TWITTER CARD:
- card: "summary_large_image"
- title: 70 chars max
- description: 200 chars max with hashtags
- image: image URL
- site: @sitename
- creator: @author
3. JSON-LD SCHEMA:
- @context: "https://schema.org"
- @type: "Article"
- headline: article title
- description: article description
- author: {{"@type": "Person", "name": "Author Name"}}
- publisher: {{"@type": "Organization", "name": "Site Name"}}
- datePublished: ISO date
- dateModified: ISO date
- mainEntityOfPage: canonical URL
- keywords: array of keywords
- wordCount: word count
Make it engaging and SEO-optimized.
"""
return prompt
def _compile_metadata_response(
self,
core_metadata: Dict[str, Any],
social_metadata: Dict[str, Any],
original_title: str
) -> Dict[str, Any]:
"""Compile final metadata response"""
try:
# Extract data from AI responses
seo_title = core_metadata.get('seo_title', original_title)
meta_description = core_metadata.get('meta_description', '')
url_slug = core_metadata.get('url_slug', '')
blog_tags = core_metadata.get('blog_tags', [])
blog_categories = core_metadata.get('blog_categories', [])
social_hashtags = core_metadata.get('social_hashtags', [])
canonical_url = core_metadata.get('canonical_url', '')
reading_time = core_metadata.get('reading_time', 0)
focus_keyword = core_metadata.get('focus_keyword', '')
open_graph = social_metadata.get('open_graph', {})
twitter_card = social_metadata.get('twitter_card', {})
json_ld_schema = social_metadata.get('json_ld_schema', {})
# Compile comprehensive response
response = {
'success': True,
'title_options': [seo_title], # For backward compatibility
'meta_descriptions': [meta_description], # For backward compatibility
'seo_title': seo_title,
'meta_description': meta_description,
'url_slug': url_slug,
'blog_tags': blog_tags,
'blog_categories': blog_categories,
'social_hashtags': social_hashtags,
'canonical_url': canonical_url,
'reading_time': reading_time,
'focus_keyword': focus_keyword,
'open_graph': open_graph,
'twitter_card': twitter_card,
'json_ld_schema': json_ld_schema,
'generated_at': datetime.utcnow().isoformat(),
'metadata_summary': {
'total_metadata_types': 10,
'ai_calls_used': 2,
'optimization_score': self._calculate_optimization_score(core_metadata, social_metadata)
}
}
logger.info(f"Metadata compilation completed. Generated {len(response)} metadata fields")
return response
except Exception as e:
logger.error(f"Metadata compilation failed: {e}")
raise e
def _calculate_optimization_score(self, core_metadata: Dict[str, Any], social_metadata: Dict[str, Any]) -> int:
"""Calculate overall optimization score for the generated metadata"""
try:
score = 0
# Check core metadata completeness
if core_metadata.get('seo_title'):
score += 15
if core_metadata.get('meta_description'):
score += 15
if core_metadata.get('url_slug'):
score += 10
if core_metadata.get('blog_tags'):
score += 10
if core_metadata.get('blog_categories'):
score += 10
if core_metadata.get('social_hashtags'):
score += 10
if core_metadata.get('focus_keyword'):
score += 10
# Check social metadata completeness
if social_metadata.get('open_graph'):
score += 10
if social_metadata.get('twitter_card'):
score += 5
if social_metadata.get('json_ld_schema'):
score += 5
return min(score, 100) # Cap at 100
except Exception as e:
logger.error(f"Failed to calculate optimization score: {e}")
return 0

View File

@@ -0,0 +1,101 @@
"""
Test script for the SEO metadata API endpoint
"""
import requests
import json
def test_seo_metadata_endpoint():
"""Test the SEO metadata API endpoint"""
# Test data
test_data = {
"content": "# The Future of AI in Content Marketing\n\nArtificial Intelligence is revolutionizing the way we create and distribute content. From automated content generation to personalized marketing campaigns, AI is transforming the content marketing landscape.\n\n## Key Benefits of AI in Content Marketing\n\n1. **Automated Content Creation**: AI can generate high-quality content at scale\n2. **Personalization**: AI enables hyper-personalized content for different audiences\n3. **Optimization**: AI helps optimize content for better performance\n4. **Analytics**: AI provides deeper insights into content performance",
"title": "The Future of AI in Content Marketing",
"research_data": {
"keyword_analysis": {
"primary": ["AI content marketing", "artificial intelligence marketing", "content automation"],
"long_tail": ["AI content marketing tools 2024", "automated content generation benefits"],
"semantic": ["machine learning", "content strategy", "digital marketing", "automation"],
"search_intent": "informational",
"target_audience": "marketing professionals",
"industry": "technology"
}
}
}
try:
print("🚀 Testing SEO Metadata API Endpoint...")
print(f"📡 Making request to: http://localhost:8000/api/blog/seo/metadata")
# Make the API request
response = requests.post(
"http://localhost:8000/api/blog/seo/metadata",
headers={"Content-Type": "application/json"},
json=test_data,
timeout=60
)
print(f"📊 Response Status: {response.status_code}")
if response.status_code == 200:
result = response.json()
print("✅ API Endpoint Test Successful!")
print("=" * 50)
# Debug: Print the full response structure
print("🔍 Full API Response Structure:")
for key, value in result.items():
if isinstance(value, dict):
print(f" {key}: {type(value)} with {len(value)} keys")
elif isinstance(value, list):
print(f" {key}: {type(value)} with {len(value)} items")
else:
print(f" {key}: {type(value)} = {value}")
print("-" * 50)
# Display key results
print(f"Success: {result.get('success', False)}")
print(f"SEO Title: {result.get('seo_title', 'N/A')}")
print(f"Meta Description: {result.get('meta_description', 'N/A')}")
print(f"URL Slug: {result.get('url_slug', 'N/A')}")
print(f"Blog Tags: {result.get('blog_tags', [])}")
print(f"Blog Categories: {result.get('blog_categories', [])}")
print(f"Social Hashtags: {result.get('social_hashtags', [])}")
print(f"Reading Time: {result.get('reading_time', 0)} minutes")
print(f"Focus Keyword: {result.get('focus_keyword', 'N/A')}")
print(f"Optimization Score: {result.get('optimization_score', 0)}%")
# Social media metadata
open_graph = result.get('open_graph', {})
twitter_card = result.get('twitter_card', {})
print(f"\n📱 Social Media Metadata:")
print(f"OG Title: {open_graph.get('title', 'N/A')}")
print(f"OG Description: {open_graph.get('description', 'N/A')}")
print(f"Twitter Title: {twitter_card.get('title', 'N/A')}")
print(f"Twitter Description: {twitter_card.get('description', 'N/A')}")
# Structured data
json_ld = result.get('json_ld_schema', {})
print(f"\n🔍 Structured Data:")
print(f"Schema Type: {json_ld.get('@type', 'N/A')}")
print(f"Headline: {json_ld.get('headline', 'N/A')}")
print(f"\n⏱️ Generated at: {result.get('generated_at', 'N/A')}")
print("🎉 API endpoint test completed successfully!")
else:
print(f"❌ API Endpoint Test Failed!")
print(f"Status Code: {response.status_code}")
print(f"Response: {response.text}")
except requests.exceptions.ConnectionError:
print("❌ Connection Error: Could not connect to the server")
print("Make sure the backend server is running on http://localhost:8000")
except requests.exceptions.Timeout:
print("❌ Timeout Error: Request took too long")
except Exception as e:
print(f"❌ Error: {e}")
if __name__ == "__main__":
test_seo_metadata_endpoint()

View File

@@ -0,0 +1,109 @@
"""
Test script for BlogSEOMetadataGenerator
Run this to verify the service works correctly
"""
import asyncio
import sys
import os
# Add the backend directory to the Python path
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from services.blog_writer.seo.blog_seo_metadata_generator import BlogSEOMetadataGenerator
async def test_metadata_generation():
"""Test the metadata generation service"""
# Sample blog content
blog_content = """
# The Future of AI in Content Marketing
Artificial Intelligence is revolutionizing the way we create and distribute content.
From automated content generation to personalized marketing campaigns, AI is transforming
the content marketing landscape.
## Key Benefits of AI in Content Marketing
1. **Automated Content Creation**: AI can generate high-quality content at scale
2. **Personalization**: AI enables hyper-personalized content for different audiences
3. **Optimization**: AI helps optimize content for better performance
4. **Analytics**: AI provides deeper insights into content performance
## The Road Ahead
As AI technology continues to evolve, we can expect even more sophisticated
content marketing tools and strategies. The future is bright for AI-powered content marketing.
"""
blog_title = "The Future of AI in Content Marketing"
# Sample research data
research_data = {
"keyword_analysis": {
"primary": ["AI content marketing", "artificial intelligence marketing", "content automation"],
"long_tail": ["AI content marketing tools 2024", "automated content generation benefits"],
"semantic": ["machine learning", "content strategy", "digital marketing", "automation"],
"search_intent": "informational",
"target_audience": "marketing professionals",
"industry": "technology"
}
}
try:
print("🚀 Testing BlogSEOMetadataGenerator...")
# Initialize the generator
generator = BlogSEOMetadataGenerator()
# Generate metadata
print("📝 Generating comprehensive SEO metadata...")
results = await generator.generate_comprehensive_metadata(
blog_content=blog_content,
blog_title=blog_title,
research_data=research_data
)
# Display results
print("\n✅ Metadata Generation Results:")
print("=" * 50)
print(f"Success: {results.get('success', False)}")
print(f"SEO Title: {results.get('seo_title', 'N/A')}")
print(f"Meta Description: {results.get('meta_description', 'N/A')}")
print(f"URL Slug: {results.get('url_slug', 'N/A')}")
print(f"Blog Tags: {results.get('blog_tags', [])}")
print(f"Blog Categories: {results.get('blog_categories', [])}")
print(f"Social Hashtags: {results.get('social_hashtags', [])}")
print(f"Reading Time: {results.get('reading_time', 0)} minutes")
print(f"Focus Keyword: {results.get('focus_keyword', 'N/A')}")
print(f"Optimization Score: {results.get('metadata_summary', {}).get('optimization_score', 0)}%")
print("\n📱 Social Media Metadata:")
print("-" * 30)
open_graph = results.get('open_graph', {})
print(f"OG Title: {open_graph.get('title', 'N/A')}")
print(f"OG Description: {open_graph.get('description', 'N/A')}")
twitter_card = results.get('twitter_card', {})
print(f"Twitter Title: {twitter_card.get('title', 'N/A')}")
print(f"Twitter Description: {twitter_card.get('description', 'N/A')}")
print("\n🔍 Structured Data:")
print("-" * 20)
json_ld = results.get('json_ld_schema', {})
print(f"Schema Type: {json_ld.get('@type', 'N/A')}")
print(f"Headline: {json_ld.get('headline', 'N/A')}")
print(f"\n⏱️ Generation completed in: {results.get('generated_at', 'N/A')}")
print("🎉 Test completed successfully!")
except Exception as e:
print(f"❌ Test failed: {e}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
asyncio.run(test_metadata_generation())