|
|
"""
|
|
|
Results Export and Reporting Module
|
|
|
Handles export of analysis results, reports, and data for external use
|
|
|
"""
|
|
|
|
|
|
import json
|
|
|
import csv
|
|
|
import io
|
|
|
import zipfile
|
|
|
import tempfile
|
|
|
import os
|
|
|
from datetime import datetime
|
|
|
from typing import Dict, Any, List, Optional, Union
|
|
|
import pandas as pd
|
|
|
from dataclasses import dataclass, asdict
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
class GEOReport:
|
|
|
"""Data class for GEO analysis reports"""
|
|
|
website_url: str
|
|
|
analysis_date: str
|
|
|
overall_score: float
|
|
|
pages_analyzed: int
|
|
|
geo_scores: Dict[str, float]
|
|
|
recommendations: List[str]
|
|
|
optimization_opportunities: List[Dict[str, Any]]
|
|
|
competitive_position: str
|
|
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
|
"""Convert report to dictionary"""
|
|
|
return asdict(self)
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
class ContentAnalysis:
|
|
|
"""Data class for content optimization analysis"""
|
|
|
original_content: str
|
|
|
analysis_date: str
|
|
|
clarity_score: float
|
|
|
structure_score: float
|
|
|
answerability_score: float
|
|
|
keywords: List[str]
|
|
|
optimized_content: Optional[str]
|
|
|
improvements_made: List[str]
|
|
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
|
"""Convert analysis to dictionary"""
|
|
|
return asdict(self)
|
|
|
|
|
|
|
|
|
class ResultExporter:
|
|
|
"""Main class for exporting analysis results and generating reports"""
|
|
|
|
|
|
def __init__(self):
|
|
|
self.export_formats = ['json', 'csv', 'html', 'pdf', 'xlsx']
|
|
|
self.supported_types = ['geo_analysis', 'content_optimization', 'qa_results', 'batch_analysis']
|
|
|
|
|
|
def export_geo_results(self, geo_results: List[Dict[str, Any]],
|
|
|
website_url: str, format_type: str = 'json') -> Union[str, bytes, Dict[str, Any]]:
|
|
|
"""
|
|
|
Export GEO analysis results in specified format
|
|
|
|
|
|
Args:
|
|
|
geo_results (List[Dict]): List of GEO analysis results
|
|
|
website_url (str): URL of analyzed website
|
|
|
format_type (str): Export format ('json', 'csv', 'html', 'xlsx')
|
|
|
|
|
|
Returns:
|
|
|
Union[str, bytes, Dict]: Exported data in requested format
|
|
|
"""
|
|
|
try:
|
|
|
|
|
|
export_data = self._prepare_geo_export_data(geo_results, website_url)
|
|
|
|
|
|
if format_type.lower() == 'json':
|
|
|
return self._export_geo_json(export_data)
|
|
|
elif format_type.lower() == 'csv':
|
|
|
return self._export_geo_csv(export_data)
|
|
|
elif format_type.lower() == 'html':
|
|
|
return self._export_geo_html(export_data)
|
|
|
elif format_type.lower() == 'xlsx':
|
|
|
return self._export_geo_excel(export_data)
|
|
|
elif format_type.lower() == 'pdf':
|
|
|
return self._export_geo_pdf(export_data)
|
|
|
else:
|
|
|
raise ValueError(f"Unsupported export format: {format_type}")
|
|
|
|
|
|
except Exception as e:
|
|
|
return {'error': f"Export failed: {str(e)}"}
|
|
|
|
|
|
def export_enhancement_results(self, enhancement_result: Dict[str, Any],
|
|
|
format_type: str = 'json') -> Union[str, bytes, Dict[str, Any]]:
|
|
|
"""
|
|
|
Export content enhancement results
|
|
|
|
|
|
Args:
|
|
|
enhancement_result (Dict): Content enhancement analysis result
|
|
|
format_type (str): Export format
|
|
|
|
|
|
Returns:
|
|
|
Union[str, bytes, Dict]: Exported data
|
|
|
"""
|
|
|
try:
|
|
|
|
|
|
export_data = self._prepare_enhancement_export_data(enhancement_result)
|
|
|
|
|
|
if format_type.lower() == 'json':
|
|
|
return json.dumps(export_data, indent=2, ensure_ascii=False)
|
|
|
elif format_type.lower() == 'html':
|
|
|
return self._export_enhancement_html(export_data)
|
|
|
elif format_type.lower() == 'csv':
|
|
|
return self._export_enhancement_csv(export_data)
|
|
|
else:
|
|
|
return json.dumps(export_data, indent=2, ensure_ascii=False)
|
|
|
|
|
|
except Exception as e:
|
|
|
return {'error': f"Enhancement export failed: {str(e)}"}
|
|
|
|
|
|
def export_qa_results(self, qa_results: List[Dict[str, Any]],
|
|
|
format_type: str = 'json') -> Union[str, bytes, Dict[str, Any]]:
|
|
|
"""
|
|
|
Export Q&A session results
|
|
|
|
|
|
Args:
|
|
|
qa_results (List[Dict]): List of Q&A interactions
|
|
|
format_type (str): Export format
|
|
|
|
|
|
Returns:
|
|
|
Union[str, bytes, Dict]: Exported data
|
|
|
"""
|
|
|
try:
|
|
|
export_data = {
|
|
|
'qa_session': {
|
|
|
'session_date': datetime.now().isoformat(),
|
|
|
'total_questions': len(qa_results),
|
|
|
'interactions': qa_results
|
|
|
},
|
|
|
'summary': {
|
|
|
'successful_answers': len([r for r in qa_results if not r.get('error')]),
|
|
|
'average_response_length': self._calculate_avg_response_length(qa_results),
|
|
|
'most_common_topics': self._extract_common_topics(qa_results)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if format_type.lower() == 'json':
|
|
|
return json.dumps(export_data, indent=2, ensure_ascii=False)
|
|
|
elif format_type.lower() == 'html':
|
|
|
return self._export_qa_html(export_data)
|
|
|
elif format_type.lower() == 'csv':
|
|
|
return self._export_qa_csv(export_data)
|
|
|
else:
|
|
|
return json.dumps(export_data, indent=2, ensure_ascii=False)
|
|
|
|
|
|
except Exception as e:
|
|
|
return {'error': f"Q&A export failed: {str(e)}"}
|
|
|
|
|
|
def create_comprehensive_report(self, analysis_data: Dict[str, Any],
|
|
|
report_type: str = 'full') -> Dict[str, Any]:
|
|
|
"""
|
|
|
Create comprehensive analysis report
|
|
|
|
|
|
Args:
|
|
|
analysis_data (Dict): Combined analysis data from multiple sources
|
|
|
report_type (str): Type of report ('full', 'summary', 'executive')
|
|
|
|
|
|
Returns:
|
|
|
Dict: Comprehensive report data
|
|
|
"""
|
|
|
try:
|
|
|
report = {
|
|
|
'report_metadata': {
|
|
|
'generated_at': datetime.now().isoformat(),
|
|
|
'report_type': report_type,
|
|
|
'generator': 'GEO SEO AI Optimizer',
|
|
|
'version': '1.0'
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if report_type == 'executive':
|
|
|
report.update(self._create_executive_summary(analysis_data))
|
|
|
elif report_type == 'summary':
|
|
|
report.update(self._create_summary_report(analysis_data))
|
|
|
else:
|
|
|
report.update(self._create_full_report(analysis_data))
|
|
|
|
|
|
return report
|
|
|
|
|
|
except Exception as e:
|
|
|
return {'error': f"Report creation failed: {str(e)}"}
|
|
|
|
|
|
def export_batch_results(self, batch_results: List[Dict[str, Any]],
|
|
|
batch_metadata: Dict[str, Any],
|
|
|
format_type: str = 'xlsx') -> Union[str, bytes, Dict[str, Any]]:
|
|
|
"""
|
|
|
Export batch analysis results
|
|
|
|
|
|
Args:
|
|
|
batch_results (List[Dict]): List of batch analysis results
|
|
|
batch_metadata (Dict): Metadata about the batch process
|
|
|
format_type (str): Export format
|
|
|
|
|
|
Returns:
|
|
|
Union[str, bytes, Dict]: Exported batch data
|
|
|
"""
|
|
|
try:
|
|
|
export_data = {
|
|
|
'batch_metadata': batch_metadata,
|
|
|
'batch_results': batch_results,
|
|
|
'batch_summary': self._create_batch_summary(batch_results),
|
|
|
'export_timestamp': datetime.now().isoformat()
|
|
|
}
|
|
|
|
|
|
if format_type.lower() == 'xlsx':
|
|
|
return self._export_batch_excel(export_data)
|
|
|
elif format_type.lower() == 'json':
|
|
|
return json.dumps(export_data, indent=2, ensure_ascii=False)
|
|
|
elif format_type.lower() == 'csv':
|
|
|
return self._export_batch_csv(export_data)
|
|
|
else:
|
|
|
return json.dumps(export_data, indent=2, ensure_ascii=False)
|
|
|
|
|
|
except Exception as e:
|
|
|
return {'error': f"Batch export failed: {str(e)}"}
|
|
|
|
|
|
def create_export_package(self, analysis_data: Dict[str, Any],
|
|
|
package_name: str = "geo_analysis") -> bytes:
|
|
|
"""
|
|
|
Create a ZIP package with multiple export formats
|
|
|
|
|
|
Args:
|
|
|
analysis_data (Dict): Analysis data to package
|
|
|
package_name (str): Name for the package
|
|
|
|
|
|
Returns:
|
|
|
bytes: ZIP file content
|
|
|
"""
|
|
|
try:
|
|
|
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
|
zip_path = os.path.join(temp_dir, f"{package_name}.zip")
|
|
|
|
|
|
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zip_file:
|
|
|
|
|
|
json_data = json.dumps(analysis_data, indent=2, ensure_ascii=False)
|
|
|
zip_file.writestr(f"{package_name}.json", json_data)
|
|
|
|
|
|
|
|
|
if 'geo_results' in analysis_data:
|
|
|
html_data = self._export_geo_html(analysis_data)
|
|
|
zip_file.writestr(f"{package_name}_report.html", html_data)
|
|
|
|
|
|
|
|
|
if 'geo_results' in analysis_data:
|
|
|
csv_data = self._export_geo_csv(analysis_data)
|
|
|
zip_file.writestr(f"{package_name}_data.csv", csv_data)
|
|
|
|
|
|
|
|
|
readme_content = self._generate_package_readme(analysis_data)
|
|
|
zip_file.writestr("README.txt", readme_content)
|
|
|
|
|
|
|
|
|
with open(zip_path, 'rb') as zip_file:
|
|
|
return zip_file.read()
|
|
|
|
|
|
except Exception as e:
|
|
|
raise Exception(f"Package creation failed: {str(e)}")
|
|
|
|
|
|
def _prepare_geo_export_data(self, geo_results: List[Dict[str, Any]], website_url: str) -> Dict[str, Any]:
|
|
|
"""Prepare GEO data for export"""
|
|
|
try:
|
|
|
|
|
|
valid_results = [r for r in geo_results if 'geo_scores' in r and not r.get('error')]
|
|
|
|
|
|
if not valid_results:
|
|
|
return {
|
|
|
'error': 'No valid GEO results to export',
|
|
|
'website_url': website_url,
|
|
|
'export_timestamp': datetime.now().isoformat()
|
|
|
}
|
|
|
|
|
|
|
|
|
all_scores = {}
|
|
|
for result in valid_results:
|
|
|
for metric, score in result.get('geo_scores', {}).items():
|
|
|
if metric not in all_scores:
|
|
|
all_scores[metric] = []
|
|
|
all_scores[metric].append(score)
|
|
|
|
|
|
avg_scores = {metric: sum(scores) / len(scores) for metric, scores in all_scores.items()}
|
|
|
overall_avg = sum(avg_scores.values()) / len(avg_scores) if avg_scores else 0
|
|
|
|
|
|
|
|
|
all_recommendations = []
|
|
|
all_opportunities = []
|
|
|
|
|
|
for result in valid_results:
|
|
|
all_recommendations.extend(result.get('recommendations', []))
|
|
|
all_opportunities.extend(result.get('optimization_opportunities', []))
|
|
|
|
|
|
|
|
|
unique_recommendations = list(set(all_recommendations))
|
|
|
|
|
|
return {
|
|
|
'website_analysis': {
|
|
|
'url': website_url,
|
|
|
'analysis_date': datetime.now().isoformat(),
|
|
|
'pages_analyzed': len(valid_results),
|
|
|
'overall_geo_score': round(overall_avg, 2)
|
|
|
},
|
|
|
'aggregate_scores': avg_scores,
|
|
|
'individual_page_results': valid_results,
|
|
|
'recommendations': unique_recommendations[:10],
|
|
|
'optimization_opportunities': all_opportunities,
|
|
|
'performance_insights': self._generate_performance_insights(avg_scores, overall_avg),
|
|
|
'export_metadata': {
|
|
|
'exported_by': 'GEO SEO AI Optimizer',
|
|
|
'export_timestamp': datetime.now().isoformat(),
|
|
|
'data_format': 'GEO Analysis Results v1.0'
|
|
|
}
|
|
|
}
|
|
|
|
|
|
except Exception as e:
|
|
|
return {'error': f"Data preparation failed: {str(e)}"}
|
|
|
|
|
|
def _prepare_enhancement_export_data(self, enhancement_result: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
"""Prepare content enhancement data for export"""
|
|
|
try:
|
|
|
scores = enhancement_result.get('scores', {})
|
|
|
|
|
|
return {
|
|
|
'content_analysis': {
|
|
|
'analysis_date': datetime.now().isoformat(),
|
|
|
'original_content_length': enhancement_result.get('original_length', 0),
|
|
|
'original_word_count': enhancement_result.get('original_word_count', 0),
|
|
|
'analysis_type': enhancement_result.get('optimization_type', 'standard')
|
|
|
},
|
|
|
'performance_scores': {
|
|
|
'clarity': scores.get('clarity', 0),
|
|
|
'structure': scores.get('structuredness', 0),
|
|
|
'answerability': scores.get('answerability', 0),
|
|
|
'overall_average': sum(scores.values()) / len(scores) if scores else 0
|
|
|
},
|
|
|
'optimization_results': {
|
|
|
'keywords_identified': enhancement_result.get('keywords', []),
|
|
|
'optimized_content': enhancement_result.get('optimized_text', ''),
|
|
|
'improvements_made': enhancement_result.get('optimization_suggestions', []),
|
|
|
'analyze_only': enhancement_result.get('analyze_only', False)
|
|
|
},
|
|
|
'export_metadata': {
|
|
|
'exported_by': 'GEO SEO AI Optimizer',
|
|
|
'export_timestamp': datetime.now().isoformat(),
|
|
|
'data_format': 'Content Enhancement Results v1.0'
|
|
|
}
|
|
|
}
|
|
|
|
|
|
except Exception as e:
|
|
|
return {'error': f"Enhancement data preparation failed: {str(e)}"}
|
|
|
|
|
|
def _export_geo_json(self, data: Dict[str, Any]) -> str:
|
|
|
"""Export GEO data as JSON"""
|
|
|
return json.dumps(data, indent=2, ensure_ascii=False)
|
|
|
|
|
|
def _export_geo_csv(self, data: Dict[str, Any]) -> str:
|
|
|
"""Export GEO data as CSV"""
|
|
|
try:
|
|
|
output = io.StringIO()
|
|
|
|
|
|
|
|
|
writer = csv.writer(output)
|
|
|
writer.writerow(['GEO Analysis Results'])
|
|
|
writer.writerow(['Website:', data.get('website_analysis', {}).get('url', 'Unknown')])
|
|
|
writer.writerow(['Analysis Date:', data.get('website_analysis', {}).get('analysis_date', 'Unknown')])
|
|
|
writer.writerow(['Overall Score:', data.get('website_analysis', {}).get('overall_geo_score', 0)])
|
|
|
writer.writerow([])
|
|
|
|
|
|
|
|
|
writer.writerow(['Metric', 'Score'])
|
|
|
for metric, score in data.get('aggregate_scores', {}).items():
|
|
|
writer.writerow([metric.replace('_', ' ').title(), round(score, 2)])
|
|
|
|
|
|
writer.writerow([])
|
|
|
writer.writerow(['Recommendations'])
|
|
|
for i, rec in enumerate(data.get('recommendations', []), 1):
|
|
|
writer.writerow([f"{i}.", rec])
|
|
|
|
|
|
|
|
|
if data.get('individual_page_results'):
|
|
|
writer.writerow([])
|
|
|
writer.writerow(['Individual Page Results'])
|
|
|
|
|
|
|
|
|
first_result = data['individual_page_results'][0]
|
|
|
if 'geo_scores' in first_result:
|
|
|
headers = ['Page Index', 'Page URL', 'Page Title'] + list(first_result['geo_scores'].keys())
|
|
|
writer.writerow(headers)
|
|
|
|
|
|
for i, result in enumerate(data['individual_page_results']):
|
|
|
page_data = result.get('page_data', {})
|
|
|
scores = result.get('geo_scores', {})
|
|
|
|
|
|
row = [
|
|
|
i + 1,
|
|
|
page_data.get('url', 'Unknown'),
|
|
|
page_data.get('title', 'Unknown')
|
|
|
] + [round(scores.get(metric, 0), 2) for metric in headers[3:]]
|
|
|
|
|
|
writer.writerow(row)
|
|
|
|
|
|
return output.getvalue()
|
|
|
|
|
|
except Exception as e:
|
|
|
return f"CSV export error: {str(e)}"
|
|
|
|
|
|
def _export_geo_html(self, data: Dict[str, Any]) -> str:
|
|
|
"""Export GEO data as HTML report"""
|
|
|
try:
|
|
|
website_info = data.get('website_analysis', {})
|
|
|
scores = data.get('aggregate_scores', {})
|
|
|
recommendations = data.get('recommendations', [])
|
|
|
|
|
|
html_content = f"""
|
|
|
<!DOCTYPE html>
|
|
|
<html lang="en">
|
|
|
<head>
|
|
|
<meta charset="UTF-8">
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
<title>GEO Analysis Report - {website_info.get('url', 'Website')}</title>
|
|
|
<style>
|
|
|
body {{
|
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
|
line-height: 1.6;
|
|
|
color: #333;
|
|
|
max-width: 1200px;
|
|
|
margin: 0 auto;
|
|
|
padding: 20px;
|
|
|
background-color: #f5f5f5;
|
|
|
}}
|
|
|
.header {{
|
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
color: white;
|
|
|
padding: 30px;
|
|
|
border-radius: 10px;
|
|
|
margin-bottom: 30px;
|
|
|
text-align: center;
|
|
|
}}
|
|
|
.header h1 {{
|
|
|
margin: 0;
|
|
|
font-size: 2.5em;
|
|
|
}}
|
|
|
.summary-cards {{
|
|
|
display: grid;
|
|
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
|
gap: 20px;
|
|
|
margin-bottom: 30px;
|
|
|
}}
|
|
|
.card {{
|
|
|
background: white;
|
|
|
padding: 20px;
|
|
|
border-radius: 10px;
|
|
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|
|
text-align: center;
|
|
|
}}
|
|
|
.card h3 {{
|
|
|
margin-top: 0;
|
|
|
color: #667eea;
|
|
|
}}
|
|
|
.score {{
|
|
|
font-size: 2em;
|
|
|
font-weight: bold;
|
|
|
color: #333;
|
|
|
}}
|
|
|
.scores-grid {{
|
|
|
display: grid;
|
|
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
|
gap: 20px;
|
|
|
margin-bottom: 30px;
|
|
|
}}
|
|
|
.score-item {{
|
|
|
background: white;
|
|
|
padding: 15px;
|
|
|
border-radius: 8px;
|
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
}}
|
|
|
.score-bar {{
|
|
|
width: 100px;
|
|
|
height: 10px;
|
|
|
background: #e0e0e0;
|
|
|
border-radius: 5px;
|
|
|
overflow: hidden;
|
|
|
}}
|
|
|
.score-fill {{
|
|
|
height: 100%;
|
|
|
background: linear-gradient(90deg, #ff6b6b, #ffa500, #4ecdc4);
|
|
|
transition: width 0.3s ease;
|
|
|
}}
|
|
|
.recommendations {{
|
|
|
background: white;
|
|
|
padding: 30px;
|
|
|
border-radius: 10px;
|
|
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|
|
margin-bottom: 30px;
|
|
|
}}
|
|
|
.recommendations h2 {{
|
|
|
color: #667eea;
|
|
|
border-bottom: 2px solid #667eea;
|
|
|
padding-bottom: 10px;
|
|
|
}}
|
|
|
.rec-item {{
|
|
|
padding: 10px 0;
|
|
|
border-bottom: 1px solid #eee;
|
|
|
}}
|
|
|
.footer {{
|
|
|
text-align: center;
|
|
|
color: #666;
|
|
|
margin-top: 40px;
|
|
|
padding-top: 20px;
|
|
|
border-top: 1px solid #ddd;
|
|
|
}}
|
|
|
</style>
|
|
|
</head>
|
|
|
<body>
|
|
|
<div class="header">
|
|
|
<h1>🚀 GEO Analysis Report</h1>
|
|
|
<p>Generative Engine Optimization Performance Analysis</p>
|
|
|
<p><strong>Website:</strong> {website_info.get('url', 'Not specified')}</p>
|
|
|
<p><strong>Analysis Date:</strong> {website_info.get('analysis_date', 'Not specified')}</p>
|
|
|
</div>
|
|
|
|
|
|
<div class="summary-cards">
|
|
|
<div class="card">
|
|
|
<h3>Overall GEO Score</h3>
|
|
|
<div class="score">{website_info.get('overall_geo_score', 0)}/10</div>
|
|
|
</div>
|
|
|
<div class="card">
|
|
|
<h3>Pages Analyzed</h3>
|
|
|
<div class="score">{website_info.get('pages_analyzed', 0)}</div>
|
|
|
</div>
|
|
|
<div class="card">
|
|
|
<h3>Recommendations</h3>
|
|
|
<div class="score">{len(recommendations)}</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<h2>📊 Detailed GEO Metrics</h2>
|
|
|
<div class="scores-grid">
|
|
|
"""
|
|
|
|
|
|
|
|
|
for metric, score in scores.items():
|
|
|
metric_display = metric.replace('_', ' ').title()
|
|
|
score_percentage = min(score * 10, 100)
|
|
|
|
|
|
html_content += f"""
|
|
|
<div class="score-item">
|
|
|
<div>
|
|
|
<strong>{metric_display}</strong><br>
|
|
|
<span style="color: #666;">{score:.1f}/10</span>
|
|
|
</div>
|
|
|
<div class="score-bar">
|
|
|
<div class="score-fill" style="width: {score_percentage}%;"></div>
|
|
|
</div>
|
|
|
</div>
|
|
|
"""
|
|
|
|
|
|
html_content += """
|
|
|
</div>
|
|
|
|
|
|
<div class="recommendations">
|
|
|
<h2>💡 Optimization Recommendations</h2>
|
|
|
"""
|
|
|
|
|
|
|
|
|
for i, rec in enumerate(recommendations, 1):
|
|
|
html_content += f'<div class="rec-item"><strong>{i}.</strong> {rec}</div>'
|
|
|
|
|
|
html_content += f"""
|
|
|
</div>
|
|
|
|
|
|
<div class="footer">
|
|
|
<p>Generated by GEO SEO AI Optimizer | {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
|
|
|
<p>This report provides AI-first SEO optimization insights for better generative engine performance.</p>
|
|
|
</div>
|
|
|
</body>
|
|
|
</html>
|
|
|
"""
|
|
|
|
|
|
return html_content
|
|
|
|
|
|
except Exception as e:
|
|
|
return f"<html><body><h1>HTML Export Error</h1><p>{str(e)}</p></body></html>"
|
|
|
|
|
|
def _export_geo_excel(self, data: Dict[str, Any]) -> bytes:
|
|
|
"""Export GEO data as Excel file"""
|
|
|
try:
|
|
|
output = io.BytesIO()
|
|
|
|
|
|
with pd.ExcelWriter(output, engine='openpyxl') as writer:
|
|
|
|
|
|
summary_data = {
|
|
|
'Metric': ['Website URL', 'Analysis Date', 'Pages Analyzed', 'Overall Score'],
|
|
|
'Value': [
|
|
|
data.get('website_analysis', {}).get('url', 'Unknown'),
|
|
|
data.get('website_analysis', {}).get('analysis_date', 'Unknown'),
|
|
|
data.get('website_analysis', {}).get('pages_analyzed', 0),
|
|
|
data.get('website_analysis', {}).get('overall_geo_score', 0)
|
|
|
]
|
|
|
}
|
|
|
pd.DataFrame(summary_data).to_excel(writer, sheet_name='Summary', index=False)
|
|
|
|
|
|
|
|
|
scores_data = []
|
|
|
for metric, score in data.get('aggregate_scores', {}).items():
|
|
|
scores_data.append({
|
|
|
'Metric': metric.replace('_', ' ').title(),
|
|
|
'Score': round(score, 2),
|
|
|
'Performance': self._get_performance_level(score)
|
|
|
})
|
|
|
|
|
|
pd.DataFrame(scores_data).to_excel(writer, sheet_name='GEO Scores', index=False)
|
|
|
|
|
|
|
|
|
rec_data = []
|
|
|
for i, rec in enumerate(data.get('recommendations', []), 1):
|
|
|
rec_data.append({
|
|
|
'Priority': i,
|
|
|
'Recommendation': rec,
|
|
|
'Category': self._categorize_recommendation(rec)
|
|
|
})
|
|
|
|
|
|
if rec_data:
|
|
|
pd.DataFrame(rec_data).to_excel(writer, sheet_name='Recommendations', index=False)
|
|
|
|
|
|
|
|
|
if data.get('individual_page_results'):
|
|
|
pages_data = []
|
|
|
for i, result in enumerate(data['individual_page_results']):
|
|
|
page_data = result.get('page_data', {})
|
|
|
scores = result.get('geo_scores', {})
|
|
|
|
|
|
page_row = {
|
|
|
'Page_Index': i + 1,
|
|
|
'URL': page_data.get('url', 'Unknown'),
|
|
|
'Title': page_data.get('title', 'Unknown'),
|
|
|
'Word_Count': page_data.get('word_count', 0)
|
|
|
}
|
|
|
|
|
|
|
|
|
for metric, score in scores.items():
|
|
|
page_row[metric.replace('_', ' ').title()] = round(score, 2)
|
|
|
|
|
|
pages_data.append(page_row)
|
|
|
|
|
|
pd.DataFrame(pages_data).to_excel(writer, sheet_name='Individual Pages', index=False)
|
|
|
|
|
|
output.seek(0)
|
|
|
return output.getvalue()
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
error_content = f"Excel export failed: {str(e)}\n\nData:\n{json.dumps(data, indent=2)}"
|
|
|
return error_content.encode('utf-8')
|
|
|
|
|
|
def _export_enhancement_html(self, data: Dict[str, Any]) -> str:
|
|
|
"""Export content enhancement results as HTML"""
|
|
|
try:
|
|
|
analysis = data.get('content_analysis', {})
|
|
|
scores = data.get('performance_scores', {})
|
|
|
optimization = data.get('optimization_results', {})
|
|
|
|
|
|
html_content = f"""
|
|
|
<!DOCTYPE html>
|
|
|
<html lang="en">
|
|
|
<head>
|
|
|
<meta charset="UTF-8">
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
<title>Content Enhancement Report</title>
|
|
|
<style>
|
|
|
body {{
|
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
|
line-height: 1.6;
|
|
|
color: #333;
|
|
|
max-width: 1000px;
|
|
|
margin: 0 auto;
|
|
|
padding: 20px;
|
|
|
background-color: #f8f9fa;
|
|
|
}}
|
|
|
.header {{
|
|
|
background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
|
|
|
color: white;
|
|
|
padding: 30px;
|
|
|
border-radius: 10px;
|
|
|
margin-bottom: 30px;
|
|
|
text-align: center;
|
|
|
}}
|
|
|
.scores {{
|
|
|
display: grid;
|
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
|
gap: 20px;
|
|
|
margin-bottom: 30px;
|
|
|
}}
|
|
|
.score-card {{
|
|
|
background: white;
|
|
|
padding: 20px;
|
|
|
border-radius: 10px;
|
|
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|
|
text-align: center;
|
|
|
}}
|
|
|
.content-section {{
|
|
|
background: white;
|
|
|
padding: 30px;
|
|
|
border-radius: 10px;
|
|
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|
|
margin-bottom: 20px;
|
|
|
}}
|
|
|
.keywords {{
|
|
|
display: flex;
|
|
|
flex-wrap: wrap;
|
|
|
gap: 10px;
|
|
|
margin-top: 15px;
|
|
|
}}
|
|
|
.keyword {{
|
|
|
background: #e9ecef;
|
|
|
padding: 5px 10px;
|
|
|
border-radius: 20px;
|
|
|
font-size: 0.9em;
|
|
|
}}
|
|
|
.optimized-content {{
|
|
|
background: #f8f9fa;
|
|
|
padding: 20px;
|
|
|
border-left: 4px solid #28a745;
|
|
|
border-radius: 5px;
|
|
|
font-style: italic;
|
|
|
}}
|
|
|
</style>
|
|
|
</head>
|
|
|
<body>
|
|
|
<div class="header">
|
|
|
<h1>🔧 Content Enhancement Report</h1>
|
|
|
<p>AI-Optimized Content Analysis Results</p>
|
|
|
<p><strong>Analysis Date:</strong> {analysis.get('analysis_date', 'Unknown')}</p>
|
|
|
</div>
|
|
|
|
|
|
<div class="scores">
|
|
|
<div class="score-card">
|
|
|
<h3>Clarity Score</h3>
|
|
|
<div style="font-size: 2em; font-weight: bold; color: #28a745;">
|
|
|
{scores.get('clarity', 0):.1f}/10
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="score-card">
|
|
|
<h3>Structure Score</h3>
|
|
|
<div style="font-size: 2em; font-weight: bold; color: #28a745;">
|
|
|
{scores.get('structure', 0):.1f}/10
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="score-card">
|
|
|
<h3>Answerability Score</h3>
|
|
|
<div style="font-size: 2em; font-weight: bold; color: #28a745;">
|
|
|
{scores.get('answerability', 0):.1f}/10
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="score-card">
|
|
|
<h3>Overall Average</h3>
|
|
|
<div style="font-size: 2em; font-weight: bold; color: #28a745;">
|
|
|
{scores.get('overall_average', 0):.1f}/10
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div class="content-section">
|
|
|
<h2>🔑 Identified Keywords</h2>
|
|
|
<div class="keywords">
|
|
|
{' '.join([f'<span class="keyword">{keyword}</span>' for keyword in optimization.get('keywords_identified', [])])}
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
{'<div class="content-section"><h2>✨ Optimized Content</h2><div class="optimized-content">' + optimization.get('optimized_content', '') + '</div></div>' if optimization.get('optimized_content') and not optimization.get('analyze_only') else ''}
|
|
|
|
|
|
<div class="content-section">
|
|
|
<h2>💡 Improvements Made</h2>
|
|
|
<ul>
|
|
|
{' '.join([f'<li>{improvement}</li>' for improvement in optimization.get('improvements_made', [])])}
|
|
|
</ul>
|
|
|
</div>
|
|
|
|
|
|
<div style="text-align: center; color: #666; margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd;">
|
|
|
<p>Generated by GEO SEO AI Optimizer | {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
|
|
|
</div>
|
|
|
</body>
|
|
|
</html>
|
|
|
"""
|
|
|
|
|
|
return html_content
|
|
|
|
|
|
except Exception as e:
|
|
|
return f"<html><body><h1>Enhancement HTML Export Error</h1><p>{str(e)}</p></body></html>"
|
|
|
|
|
|
def _export_enhancement_csv(self, data: Dict[str, Any]) -> str:
|
|
|
"""Export content enhancement results as CSV"""
|
|
|
try:
|
|
|
output = io.StringIO()
|
|
|
writer = csv.writer(output)
|
|
|
|
|
|
|
|
|
analysis = data.get('content_analysis', {})
|
|
|
scores = data.get('performance_scores', {})
|
|
|
optimization = data.get('optimization_results', {})
|
|
|
|
|
|
writer.writerow(['Content Enhancement Analysis Report'])
|
|
|
writer.writerow(['Analysis Date:', analysis.get('analysis_date', 'Unknown')])
|
|
|
writer.writerow(['Original Content Length:', analysis.get('original_content_length', 0)])
|
|
|
writer.writerow(['Original Word Count:', analysis.get('original_word_count', 0)])
|
|
|
writer.writerow([])
|
|
|
|
|
|
|
|
|
writer.writerow(['Performance Scores'])
|
|
|
writer.writerow(['Metric', 'Score'])
|
|
|
for metric, score in scores.items():
|
|
|
writer.writerow([metric.replace('_', ' ').title(), round(score, 2)])
|
|
|
|
|
|
writer.writerow([])
|
|
|
writer.writerow(['Keywords Identified'])
|
|
|
for keyword in optimization.get('keywords_identified', []):
|
|
|
writer.writerow([keyword])
|
|
|
|
|
|
writer.writerow([])
|
|
|
writer.writerow(['Improvements Made'])
|
|
|
for improvement in optimization.get('improvements_made', []):
|
|
|
writer.writerow([improvement])
|
|
|
|
|
|
return output.getvalue()
|
|
|
|
|
|
except Exception as e:
|
|
|
return f"Enhancement CSV export error: {str(e)}"
|
|
|
|
|
|
def _export_qa_html(self, data: Dict[str, Any]) -> str:
|
|
|
"""Export Q&A results as HTML"""
|
|
|
try:
|
|
|
session = data.get('qa_session', {})
|
|
|
summary = data.get('summary', {})
|
|
|
interactions = session.get('interactions', [])
|
|
|
|
|
|
html_content = f"""
|
|
|
<!DOCTYPE html>
|
|
|
<html lang="en">
|
|
|
<head>
|
|
|
<meta charset="UTF-8">
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
<title>Q&A Session Report</title>
|
|
|
<style>
|
|
|
body {{
|
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
|
line-height: 1.6;
|
|
|
color: #333;
|
|
|
max-width: 1000px;
|
|
|
margin: 0 auto;
|
|
|
padding: 20px;
|
|
|
background-color: #f8f9fa;
|
|
|
}}
|
|
|
.header {{
|
|
|
background: linear-gradient(135deg, #6f42c1 0%, #e83e8c 100%);
|
|
|
color: white;
|
|
|
padding: 30px;
|
|
|
border-radius: 10px;
|
|
|
margin-bottom: 30px;
|
|
|
text-align: center;
|
|
|
}}
|
|
|
.summary {{
|
|
|
display: grid;
|
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
|
gap: 20px;
|
|
|
margin-bottom: 30px;
|
|
|
}}
|
|
|
.summary-card {{
|
|
|
background: white;
|
|
|
padding: 20px;
|
|
|
border-radius: 10px;
|
|
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|
|
text-align: center;
|
|
|
}}
|
|
|
.qa-item {{
|
|
|
background: white;
|
|
|
padding: 20px;
|
|
|
border-radius: 10px;
|
|
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
|
|
margin-bottom: 20px;
|
|
|
}}
|
|
|
.question {{
|
|
|
background: #e9ecef;
|
|
|
padding: 15px;
|
|
|
border-left: 4px solid #6f42c1;
|
|
|
border-radius: 5px;
|
|
|
margin-bottom: 15px;
|
|
|
}}
|
|
|
.answer {{
|
|
|
padding: 15px;
|
|
|
border-left: 4px solid #28a745;
|
|
|
border-radius: 5px;
|
|
|
background: #f8f9fa;
|
|
|
}}
|
|
|
.sources {{
|
|
|
margin-top: 15px;
|
|
|
padding: 10px;
|
|
|
background: #fff3cd;
|
|
|
border-radius: 5px;
|
|
|
font-size: 0.9em;
|
|
|
}}
|
|
|
</style>
|
|
|
</head>
|
|
|
<body>
|
|
|
<div class="header">
|
|
|
<h1>💬 Q&A Session Report</h1>
|
|
|
<p>Document Question & Answer Analysis</p>
|
|
|
<p><strong>Session Date:</strong> {session.get('session_date', 'Unknown')}</p>
|
|
|
</div>
|
|
|
|
|
|
<div class="summary">
|
|
|
<div class="summary-card">
|
|
|
<h3>Total Questions</h3>
|
|
|
<div style="font-size: 2em; font-weight: bold; color: #6f42c1;">
|
|
|
{session.get('total_questions', 0)}
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="summary-card">
|
|
|
<h3>Successful Answers</h3>
|
|
|
<div style="font-size: 2em; font-weight: bold; color: #28a745;">
|
|
|
{summary.get('successful_answers', 0)}
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="summary-card">
|
|
|
<h3>Avg Response Length</h3>
|
|
|
<div style="font-size: 2em; font-weight: bold; color: #17a2b8;">
|
|
|
{summary.get('average_response_length', 0):.0f}
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<h2>📝 Q&A Interactions</h2>
|
|
|
"""
|
|
|
|
|
|
|
|
|
for i, interaction in enumerate(interactions, 1):
|
|
|
question = interaction.get('query', 'No question')
|
|
|
answer = interaction.get('result', interaction.get('answer', 'No answer'))
|
|
|
sources = interaction.get('sources', [])
|
|
|
|
|
|
html_content += f"""
|
|
|
<div class="qa-item">
|
|
|
<h3>Question {i}</h3>
|
|
|
<div class="question">
|
|
|
<strong>Q:</strong> {question}
|
|
|
</div>
|
|
|
<div class="answer">
|
|
|
<strong>A:</strong> {answer}
|
|
|
</div>
|
|
|
"""
|
|
|
|
|
|
if sources:
|
|
|
html_content += '<div class="sources"><strong>Sources:</strong><ul>'
|
|
|
for source in sources[:3]:
|
|
|
content_preview = source.get('content', '')[:200] + '...' if len(source.get('content', '')) > 200 else source.get('content', '')
|
|
|
html_content += f'<li>{content_preview}</li>'
|
|
|
html_content += '</ul></div>'
|
|
|
|
|
|
html_content += '</div>'
|
|
|
|
|
|
html_content += f"""
|
|
|
|
|
|
<div style="text-align: center; color: #666; margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd;">
|
|
|
<p>Generated by GEO SEO AI Optimizer | {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
|
|
|
</div>
|
|
|
</body>
|
|
|
</html>
|
|
|
"""
|
|
|
|
|
|
return html_content
|
|
|
|
|
|
except Exception as e:
|
|
|
return f"<html><body><h1>Q&A HTML Export Error</h1><p>{str(e)}</p></body></html>"
|
|
|
|
|
|
def _export_qa_csv(self, data: Dict[str, Any]) -> str:
|
|
|
"""Export Q&A results as CSV"""
|
|
|
try:
|
|
|
output = io.StringIO()
|
|
|
writer = csv.writer(output)
|
|
|
|
|
|
session = data.get('qa_session', {})
|
|
|
summary = data.get('summary', {})
|
|
|
interactions = session.get('interactions', [])
|
|
|
|
|
|
|
|
|
writer.writerow(['Q&A Session Report'])
|
|
|
writer.writerow(['Session Date:', session.get('session_date', 'Unknown')])
|
|
|
writer.writerow(['Total Questions:', session.get('total_questions', 0)])
|
|
|
writer.writerow(['Successful Answers:', summary.get('successful_answers', 0)])
|
|
|
writer.writerow([])
|
|
|
|
|
|
|
|
|
writer.writerow(['Question Index', 'Question', 'Answer', 'Has Sources', 'Answer Length'])
|
|
|
|
|
|
for i, interaction in enumerate(interactions, 1):
|
|
|
question = interaction.get('query', 'No question')
|
|
|
answer = interaction.get('result', interaction.get('answer', 'No answer'))
|
|
|
has_sources = 'Yes' if interaction.get('sources') else 'No'
|
|
|
answer_length = len(answer) if answer else 0
|
|
|
|
|
|
writer.writerow([i, question, answer, has_sources, answer_length])
|
|
|
|
|
|
return output.getvalue()
|
|
|
|
|
|
except Exception as e:
|
|
|
return f"Q&A CSV export error: {str(e)}"
|
|
|
|
|
|
def _export_batch_excel(self, data: Dict[str, Any]) -> bytes:
|
|
|
"""Export batch results as Excel file"""
|
|
|
try:
|
|
|
output = io.BytesIO()
|
|
|
|
|
|
with pd.ExcelWriter(output, engine='openpyxl') as writer:
|
|
|
|
|
|
metadata = data.get('batch_metadata', {})
|
|
|
metadata_df = pd.DataFrame([
|
|
|
{'Property': k, 'Value': v} for k, v in metadata.items()
|
|
|
])
|
|
|
metadata_df.to_excel(writer, sheet_name='Batch Metadata', index=False)
|
|
|
|
|
|
|
|
|
summary = data.get('batch_summary', {})
|
|
|
summary_df = pd.DataFrame([
|
|
|
{'Metric': k, 'Value': v} for k, v in summary.items()
|
|
|
])
|
|
|
summary_df.to_excel(writer, sheet_name='Batch Summary', index=False)
|
|
|
|
|
|
|
|
|
results = data.get('batch_results', [])
|
|
|
if results:
|
|
|
|
|
|
flattened_results = []
|
|
|
for i, result in enumerate(results):
|
|
|
flat_result = {'Batch_Index': i}
|
|
|
self._flatten_dict(result, flat_result)
|
|
|
flattened_results.append(flat_result)
|
|
|
|
|
|
results_df = pd.DataFrame(flattened_results)
|
|
|
results_df.to_excel(writer, sheet_name='Batch Results', index=False)
|
|
|
|
|
|
output.seek(0)
|
|
|
return output.getvalue()
|
|
|
|
|
|
except Exception as e:
|
|
|
error_content = f"Batch Excel export failed: {str(e)}\n\nData:\n{json.dumps(data, indent=2)}"
|
|
|
return error_content.encode('utf-8')
|
|
|
|
|
|
def _export_batch_csv(self, data: Dict[str, Any]) -> str:
|
|
|
"""Export batch results as CSV"""
|
|
|
try:
|
|
|
output = io.StringIO()
|
|
|
writer = csv.writer(output)
|
|
|
|
|
|
|
|
|
metadata = data.get('batch_metadata', {})
|
|
|
writer.writerow(['Batch Analysis Results'])
|
|
|
writer.writerow(['Export Timestamp:', data.get('export_timestamp', 'Unknown')])
|
|
|
writer.writerow([])
|
|
|
|
|
|
writer.writerow(['Batch Metadata'])
|
|
|
for key, value in metadata.items():
|
|
|
writer.writerow([key, value])
|
|
|
|
|
|
writer.writerow([])
|
|
|
|
|
|
|
|
|
summary = data.get('batch_summary', {})
|
|
|
writer.writerow(['Batch Summary'])
|
|
|
for key, value in summary.items():
|
|
|
writer.writerow([key, value])
|
|
|
|
|
|
writer.writerow([])
|
|
|
|
|
|
|
|
|
results = data.get('batch_results', [])
|
|
|
if results:
|
|
|
writer.writerow(['Individual Results'])
|
|
|
writer.writerow(['Index', 'Status', 'Summary'])
|
|
|
|
|
|
for i, result in enumerate(results):
|
|
|
status = 'Success' if not result.get('error') else 'Error'
|
|
|
summary_text = str(result)[:100] + '...' if len(str(result)) > 100 else str(result)
|
|
|
writer.writerow([i, status, summary_text])
|
|
|
|
|
|
return output.getvalue()
|
|
|
|
|
|
except Exception as e:
|
|
|
return f"Batch CSV export error: {str(e)}"
|
|
|
|
|
|
def _export_geo_pdf(self, data: Dict[str, Any]) -> bytes:
|
|
|
"""Export GEO data as PDF (placeholder - would need reportlab)"""
|
|
|
try:
|
|
|
|
|
|
|
|
|
html_content = self._export_geo_html(data)
|
|
|
return html_content.encode('utf-8')
|
|
|
|
|
|
except Exception as e:
|
|
|
error_content = f"PDF export not fully implemented. Error: {str(e)}"
|
|
|
return error_content.encode('utf-8')
|
|
|
|
|
|
def _create_executive_summary(self, analysis_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
"""Create executive summary report"""
|
|
|
try:
|
|
|
geo_results = analysis_data.get('geo_results', [])
|
|
|
enhancement_results = analysis_data.get('enhancement_results', {})
|
|
|
qa_results = analysis_data.get('qa_results', [])
|
|
|
|
|
|
|
|
|
overall_performance = self._calculate_overall_performance(analysis_data)
|
|
|
|
|
|
return {
|
|
|
'executive_summary': {
|
|
|
'overall_performance_score': overall_performance,
|
|
|
'key_findings': self._extract_key_findings(analysis_data),
|
|
|
'priority_recommendations': self._get_priority_recommendations(analysis_data),
|
|
|
'roi_potential': self._estimate_roi_potential(overall_performance),
|
|
|
'implementation_timeline': self._suggest_implementation_timeline(analysis_data),
|
|
|
'resource_requirements': self._estimate_resource_requirements(analysis_data)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
except Exception as e:
|
|
|
return {'error': f"Executive summary creation failed: {str(e)}"}
|
|
|
|
|
|
def _create_summary_report(self, analysis_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
"""Create summary report"""
|
|
|
try:
|
|
|
return {
|
|
|
'summary_report': {
|
|
|
'analysis_overview': self._create_analysis_overview(analysis_data),
|
|
|
'performance_metrics': self._summarize_performance_metrics(analysis_data),
|
|
|
'improvement_opportunities': self._identify_improvement_opportunities(analysis_data),
|
|
|
'competitive_position': self._assess_competitive_position(analysis_data),
|
|
|
'next_steps': self._recommend_next_steps(analysis_data)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
except Exception as e:
|
|
|
return {'error': f"Summary report creation failed: {str(e)}"}
|
|
|
|
|
|
def _create_full_report(self, analysis_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
"""Create full detailed report"""
|
|
|
try:
|
|
|
return {
|
|
|
'full_report': {
|
|
|
'executive_summary': self._create_executive_summary(analysis_data).get('executive_summary', {}),
|
|
|
'detailed_analysis': {
|
|
|
'geo_analysis_details': analysis_data.get('geo_results', []),
|
|
|
'content_optimization_details': analysis_data.get('enhancement_results', {}),
|
|
|
'qa_performance_details': analysis_data.get('qa_results', [])
|
|
|
},
|
|
|
'methodology': self._document_methodology(),
|
|
|
'data_sources': self._document_data_sources(analysis_data),
|
|
|
'limitations': self._document_limitations(),
|
|
|
'appendices': self._create_appendices(analysis_data)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
except Exception as e:
|
|
|
return {'error': f"Full report creation failed: {str(e)}"}
|
|
|
|
|
|
def _create_batch_summary(self, batch_results: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
|
"""Create summary of batch processing results"""
|
|
|
try:
|
|
|
total_items = len(batch_results)
|
|
|
successful_items = len([r for r in batch_results if not r.get('error')])
|
|
|
failed_items = total_items - successful_items
|
|
|
|
|
|
return {
|
|
|
'total_items': total_items,
|
|
|
'successful_items': successful_items,
|
|
|
'failed_items': failed_items,
|
|
|
'success_rate': (successful_items / total_items * 100) if total_items > 0 else 0,
|
|
|
'processing_status': 'Completed',
|
|
|
'average_processing_time': self._calculate_avg_processing_time(batch_results),
|
|
|
'common_errors': self._identify_common_errors(batch_results)
|
|
|
}
|
|
|
|
|
|
except Exception as e:
|
|
|
return {'error': f"Batch summary creation failed: {str(e)}"}
|
|
|
|
|
|
def _generate_performance_insights(self, scores: Dict[str, float], overall_avg: float) -> List[str]:
|
|
|
"""Generate performance insights from scores"""
|
|
|
insights = []
|
|
|
|
|
|
try:
|
|
|
|
|
|
if overall_avg >= 8.0:
|
|
|
insights.append("Excellent overall GEO performance - content is well-optimized for AI search engines")
|
|
|
elif overall_avg >= 6.0:
|
|
|
insights.append("Good GEO performance with room for improvement in specific areas")
|
|
|
elif overall_avg >= 4.0:
|
|
|
insights.append("Moderate GEO performance - significant optimization opportunities exist")
|
|
|
else:
|
|
|
insights.append("Low GEO performance - comprehensive optimization needed")
|
|
|
|
|
|
|
|
|
for metric, score in scores.items():
|
|
|
if score < 5.0:
|
|
|
metric_name = metric.replace('_', ' ').title()
|
|
|
insights.append(f"Low {metric_name} score ({score:.1f}) needs immediate attention")
|
|
|
elif score >= 8.5:
|
|
|
metric_name = metric.replace('_', ' ').title()
|
|
|
insights.append(f"Excellent {metric_name} score ({score:.1f}) - maintain current approach")
|
|
|
|
|
|
return insights[:5]
|
|
|
|
|
|
except Exception:
|
|
|
return ["Unable to generate performance insights"]
|
|
|
|
|
|
def _generate_package_readme(self, analysis_data: Dict[str, Any]) -> str:
|
|
|
"""Generate README file for export package"""
|
|
|
try:
|
|
|
readme_content = f"""
|
|
|
GEO SEO AI Optimizer - Analysis Package
|
|
|
======================================
|
|
|
|
|
|
Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
|
|
|
|
|
This package contains the complete analysis results from the GEO SEO AI Optimizer tool.
|
|
|
|
|
|
Files Included:
|
|
|
- JSON file: Complete raw data in JSON format
|
|
|
- HTML file: Visual report for web viewing
|
|
|
- CSV file: Tabular data for spreadsheet analysis
|
|
|
- README.txt: This file
|
|
|
|
|
|
About GEO (Generative Engine Optimization):
|
|
|
GEO is the practice of optimizing content for AI-powered search engines and
|
|
|
language models. Unlike traditional SEO, GEO focuses on:
|
|
|
|
|
|
- AI search visibility
|
|
|
- Query intent matching
|
|
|
- Conversational readiness
|
|
|
- Citation worthiness
|
|
|
- Semantic richness
|
|
|
- Context completeness
|
|
|
|
|
|
How to Use These Files:
|
|
|
1. Open the HTML file in a web browser for a visual report
|
|
|
2. Import the CSV file into Excel or Google Sheets for analysis
|
|
|
3. Use the JSON file for programmatic processing or integration
|
|
|
|
|
|
For more information about GEO optimization, visit the tool documentation.
|
|
|
|
|
|
Generated by: GEO SEO AI Optimizer v1.0
|
|
|
"""
|
|
|
return readme_content
|
|
|
|
|
|
except Exception as e:
|
|
|
return f"README generation failed: {str(e)}"
|
|
|
|
|
|
|
|
|
|
|
|
def _get_performance_level(self, score: float) -> str:
|
|
|
"""Get performance level description for a score"""
|
|
|
if score >= 8.0:
|
|
|
return "Excellent"
|
|
|
elif score >= 6.0:
|
|
|
return "Good"
|
|
|
elif score >= 4.0:
|
|
|
return "Fair"
|
|
|
else:
|
|
|
return "Needs Improvement"
|
|
|
|
|
|
def _categorize_recommendation(self, recommendation: str) -> str:
|
|
|
"""Categorize a recommendation based on content"""
|
|
|
rec_lower = recommendation.lower()
|
|
|
|
|
|
if any(word in rec_lower for word in ['structure', 'heading', 'format']):
|
|
|
return "Content Structure"
|
|
|
elif any(word in rec_lower for word in ['keyword', 'semantic', 'topic']):
|
|
|
return "SEO & Keywords"
|
|
|
elif any(word in rec_lower for word in ['clarity', 'readability', 'language']):
|
|
|
return "Content Quality"
|
|
|
elif any(word in rec_lower for word in ['technical', 'schema', 'markup']):
|
|
|
return "Technical SEO"
|
|
|
else:
|
|
|
return "General"
|
|
|
|
|
|
def _calculate_avg_response_length(self, qa_results: List[Dict[str, Any]]) -> float:
|
|
|
"""Calculate average response length for Q&A results"""
|
|
|
try:
|
|
|
response_lengths = []
|
|
|
for result in qa_results:
|
|
|
answer = result.get('result', result.get('answer', ''))
|
|
|
if answer and not result.get('error'):
|
|
|
response_lengths.append(len(answer))
|
|
|
|
|
|
return sum(response_lengths) / len(response_lengths) if response_lengths else 0
|
|
|
|
|
|
except Exception:
|
|
|
return 0
|
|
|
|
|
|
def _extract_common_topics(self, qa_results: List[Dict[str, Any]]) -> List[str]:
|
|
|
"""Extract common topics from Q&A results"""
|
|
|
try:
|
|
|
|
|
|
topics = {}
|
|
|
|
|
|
for result in qa_results:
|
|
|
question = result.get('query', result.get('question', ''))
|
|
|
if question:
|
|
|
words = question.lower().split()
|
|
|
for word in words:
|
|
|
if len(word) > 4:
|
|
|
topics[word] = topics.get(word, 0) + 1
|
|
|
|
|
|
|
|
|
sorted_topics = sorted(topics.items(), key=lambda x: x[1], reverse=True)
|
|
|
return [topic for topic, count in sorted_topics[:5]]
|
|
|
|
|
|
except Exception:
|
|
|
return []
|
|
|
|
|
|
def _flatten_dict(self, d: Dict[str, Any], parent_dict: Dict[str, Any], parent_key: str = '') -> None:
|
|
|
"""Flatten nested dictionary for tabular export"""
|
|
|
try:
|
|
|
for key, value in d.items():
|
|
|
new_key = f"{parent_key}_{key}" if parent_key else key
|
|
|
|
|
|
if isinstance(value, dict):
|
|
|
self._flatten_dict(value, parent_dict, new_key)
|
|
|
elif isinstance(value, list):
|
|
|
parent_dict[new_key] = json.dumps(value)
|
|
|
else:
|
|
|
parent_dict[new_key] = value
|
|
|
|
|
|
except Exception:
|
|
|
pass
|
|
|
|
|
|
def _calculate_overall_performance(self, analysis_data: Dict[str, Any]) -> float:
|
|
|
"""Calculate overall performance score across all analyses"""
|
|
|
try:
|
|
|
scores = []
|
|
|
|
|
|
|
|
|
geo_results = analysis_data.get('geo_results', [])
|
|
|
for result in geo_results:
|
|
|
if 'geo_scores' in result:
|
|
|
geo_score_values = list(result['geo_scores'].values())
|
|
|
if geo_score_values:
|
|
|
scores.append(sum(geo_score_values) / len(geo_score_values))
|
|
|
|
|
|
|
|
|
enhancement = analysis_data.get('enhancement_results', {})
|
|
|
if 'scores' in enhancement:
|
|
|
enh_scores = list(enhancement['scores'].values())
|
|
|
if enh_scores:
|
|
|
scores.append(sum(enh_scores) / len(enh_scores))
|
|
|
|
|
|
return sum(scores) / len(scores) if scores else 0
|
|
|
|
|
|
except Exception:
|
|
|
return 0
|
|
|
|
|
|
def _extract_key_findings(self, analysis_data: Dict[str, Any]) -> List[str]:
|
|
|
"""Extract key findings from analysis data"""
|
|
|
findings = []
|
|
|
|
|
|
try:
|
|
|
|
|
|
overall_perf = self._calculate_overall_performance(analysis_data)
|
|
|
|
|
|
if overall_perf >= 8.0:
|
|
|
findings.append("Content demonstrates excellent AI search optimization")
|
|
|
elif overall_perf <= 4.0:
|
|
|
findings.append("Significant optimization opportunities identified")
|
|
|
|
|
|
|
|
|
geo_results = analysis_data.get('geo_results', [])
|
|
|
if geo_results:
|
|
|
findings.append(f"Analyzed {len(geo_results)} pages for GEO performance")
|
|
|
|
|
|
enhancement = analysis_data.get('enhancement_results', {})
|
|
|
if enhancement and 'keywords' in enhancement:
|
|
|
findings.append(f"Identified {len(enhancement['keywords'])} key optimization terms")
|
|
|
|
|
|
return findings[:5]
|
|
|
|
|
|
except Exception:
|
|
|
return ["Unable to extract key findings"]
|
|
|
|
|
|
def _get_priority_recommendations(self, analysis_data: Dict[str, Any]) -> List[str]:
|
|
|
"""Get priority recommendations from analysis"""
|
|
|
try:
|
|
|
recommendations = []
|
|
|
|
|
|
|
|
|
geo_results = analysis_data.get('geo_results', [])
|
|
|
for result in geo_results:
|
|
|
recommendations.extend(result.get('recommendations', []))
|
|
|
|
|
|
|
|
|
unique_recs = list(set(recommendations))
|
|
|
return unique_recs[:3]
|
|
|
|
|
|
except Exception:
|
|
|
return ["Review and implement GEO best practices"]
|
|
|
|
|
|
def _estimate_roi_potential(self, performance_score: float) -> str:
|
|
|
"""Estimate ROI potential based on performance score"""
|
|
|
if performance_score <= 4.0:
|
|
|
return "High - Significant improvement potential"
|
|
|
elif performance_score <= 6.0:
|
|
|
return "Medium - Moderate improvement opportunities"
|
|
|
else:
|
|
|
return "Low - Already well-optimized"
|
|
|
|
|
|
def _suggest_implementation_timeline(self, analysis_data: Dict[str, Any]) -> str:
|
|
|
"""Suggest implementation timeline"""
|
|
|
try:
|
|
|
overall_perf = self._calculate_overall_performance(analysis_data)
|
|
|
|
|
|
if overall_perf <= 4.0:
|
|
|
return "3-6 months for comprehensive optimization"
|
|
|
elif overall_perf <= 6.0:
|
|
|
return "1-3 months for targeted improvements"
|
|
|
else:
|
|
|
return "Ongoing maintenance and monitoring"
|
|
|
|
|
|
except Exception:
|
|
|
return "Timeline assessment unavailable"
|
|
|
|
|
|
def _estimate_resource_requirements(self, analysis_data: Dict[str, Any]) -> Dict[str, str]:
|
|
|
"""Estimate resource requirements"""
|
|
|
return {
|
|
|
'content_team': 'Required for content optimization',
|
|
|
'technical_team': 'Required for technical implementations',
|
|
|
'timeline': self._suggest_implementation_timeline(analysis_data),
|
|
|
'budget': 'Varies based on scope of optimizations'
|
|
|
}
|
|
|
|
|
|
def _create_analysis_overview(self, analysis_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
"""Create analysis overview"""
|
|
|
try:
|
|
|
return {
|
|
|
'analyses_performed': list(analysis_data.keys()),
|
|
|
'total_items_analyzed': sum(len(v) if isinstance(v, list) else 1 for v in analysis_data.values()),
|
|
|
'analysis_scope': 'Comprehensive GEO and content optimization analysis',
|
|
|
'key_focus_areas': ['AI Search Optimization', 'Content Enhancement', 'Performance Analysis']
|
|
|
}
|
|
|
|
|
|
except Exception:
|
|
|
return {'error': 'Overview creation failed'}
|
|
|
|
|
|
def _summarize_performance_metrics(self, analysis_data: Dict[str, Any]) -> Dict[str, float]:
|
|
|
"""Summarize performance metrics"""
|
|
|
try:
|
|
|
return {
|
|
|
'overall_performance': self._calculate_overall_performance(analysis_data),
|
|
|
'optimization_potential': 10 - self._calculate_overall_performance(analysis_data),
|
|
|
'completion_rate': 100.0
|
|
|
}
|
|
|
|
|
|
except Exception:
|
|
|
return {}
|
|
|
|
|
|
def _identify_improvement_opportunities(self, analysis_data: Dict[str, Any]) -> List[str]:
|
|
|
"""Identify improvement opportunities"""
|
|
|
return self._get_priority_recommendations(analysis_data)
|
|
|
|
|
|
def _assess_competitive_position(self, analysis_data: Dict[str, Any]) -> str:
|
|
|
"""Assess competitive position"""
|
|
|
try:
|
|
|
overall_perf = self._calculate_overall_performance(analysis_data)
|
|
|
|
|
|
if overall_perf >= 8.0:
|
|
|
return "Strong - Above average GEO performance"
|
|
|
elif overall_perf >= 6.0:
|
|
|
return "Competitive - Meeting industry standards"
|
|
|
elif overall_perf >= 4.0:
|
|
|
return "Below Average - Improvement needed"
|
|
|
else:
|
|
|
return "Weak - Significant optimization required"
|
|
|
|
|
|
except Exception:
|
|
|
return "Assessment unavailable"
|
|
|
|
|
|
def _recommend_next_steps(self, analysis_data: Dict[str, Any]) -> List[str]:
|
|
|
"""Recommend next steps"""
|
|
|
steps = [
|
|
|
"Review detailed analysis results",
|
|
|
"Prioritize recommendations by impact",
|
|
|
"Develop implementation plan",
|
|
|
"Monitor performance improvements"
|
|
|
]
|
|
|
|
|
|
|
|
|
overall_perf = self._calculate_overall_performance(analysis_data)
|
|
|
if overall_perf <= 4.0:
|
|
|
steps.insert(1, "Focus on fundamental GEO optimization")
|
|
|
|
|
|
return steps
|
|
|
|
|
|
def _document_methodology(self) -> Dict[str, str]:
|
|
|
"""Document analysis methodology"""
|
|
|
return {
|
|
|
'geo_analysis': 'AI-powered content analysis using specialized GEO metrics',
|
|
|
'content_optimization': 'LLM-based content enhancement and scoring',
|
|
|
'performance_scoring': 'Multi-dimensional scoring system for AI search optimization',
|
|
|
'data_collection': 'Automated content parsing and analysis',
|
|
|
'validation': 'Cross-referenced metrics and quality assurance checks'
|
|
|
}
|
|
|
|
|
|
def _document_data_sources(self, analysis_data: Dict[str, Any]) -> List[str]:
|
|
|
"""Document data sources used in analysis"""
|
|
|
sources = []
|
|
|
|
|
|
if 'geo_results' in analysis_data:
|
|
|
sources.append("Website content analysis")
|
|
|
if 'enhancement_results' in analysis_data:
|
|
|
sources.append("Content optimization analysis")
|
|
|
if 'qa_results' in analysis_data:
|
|
|
sources.append("Document Q&A interactions")
|
|
|
|
|
|
sources.extend([
|
|
|
"AI-powered content scoring",
|
|
|
"GEO performance metrics",
|
|
|
"Industry best practices database"
|
|
|
])
|
|
|
|
|
|
return sources
|
|
|
|
|
|
def _document_limitations(self) -> List[str]:
|
|
|
"""Document analysis limitations"""
|
|
|
return [
|
|
|
"Analysis based on current content snapshot",
|
|
|
"Performance may vary with search engine algorithm updates",
|
|
|
"Recommendations require human review for implementation",
|
|
|
"Results depend on quality of input content",
|
|
|
"AI model performance may vary across different content types"
|
|
|
]
|
|
|
|
|
|
def _create_appendices(self, analysis_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
"""Create report appendices"""
|
|
|
try:
|
|
|
return {
|
|
|
'technical_details': {
|
|
|
'models_used': ['GPT-based content analysis', 'Semantic similarity scoring'],
|
|
|
'processing_time': 'Variable based on content volume',
|
|
|
'confidence_intervals': 'Scores provided with ±0.5 accuracy'
|
|
|
},
|
|
|
'glossary': {
|
|
|
'GEO': 'Generative Engine Optimization - optimization for AI search engines',
|
|
|
'AI Search Visibility': 'Likelihood of content appearing in AI search results',
|
|
|
'Citation Worthiness': 'Probability of content being cited by AI systems',
|
|
|
'Conversational Readiness': 'Suitability for AI chat responses'
|
|
|
},
|
|
|
'references': [
|
|
|
'GEO Best Practices Guide',
|
|
|
'AI Search Engine Optimization Standards',
|
|
|
'Content Performance Benchmarks'
|
|
|
]
|
|
|
}
|
|
|
|
|
|
except Exception:
|
|
|
return {}
|
|
|
|
|
|
def _calculate_avg_processing_time(self, batch_results: List[Dict[str, Any]]) -> float:
|
|
|
"""Calculate average processing time for batch results"""
|
|
|
try:
|
|
|
processing_times = []
|
|
|
|
|
|
for result in batch_results:
|
|
|
if 'processing_time' in result:
|
|
|
processing_times.append(result['processing_time'])
|
|
|
|
|
|
return sum(processing_times) / len(processing_times) if processing_times else 0
|
|
|
|
|
|
except Exception:
|
|
|
return 0
|
|
|
|
|
|
def _identify_common_errors(self, batch_results: List[Dict[str, Any]]) -> List[str]:
|
|
|
"""Identify common errors in batch processing"""
|
|
|
try:
|
|
|
error_counts = {}
|
|
|
|
|
|
for result in batch_results:
|
|
|
if result.get('error'):
|
|
|
error_msg = str(result['error'])[:50]
|
|
|
error_counts[error_msg] = error_counts.get(error_msg, 0) + 1
|
|
|
|
|
|
|
|
|
sorted_errors = sorted(error_counts.items(), key=lambda x: x[1], reverse=True)
|
|
|
return [error for error, count in sorted_errors[:3]]
|
|
|
|
|
|
except Exception:
|
|
|
return []
|
|
|
|
|
|
|
|
|
class DataValidator:
|
|
|
"""Helper class for validating export data"""
|
|
|
|
|
|
@staticmethod
|
|
|
def validate_geo_data(geo_results: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
|
"""Validate GEO analysis data structure"""
|
|
|
validation_result = {
|
|
|
'valid': True,
|
|
|
'errors': [],
|
|
|
'warnings': []
|
|
|
}
|
|
|
|
|
|
try:
|
|
|
if not geo_results:
|
|
|
validation_result['errors'].append("No GEO results provided")
|
|
|
validation_result['valid'] = False
|
|
|
return validation_result
|
|
|
|
|
|
for i, result in enumerate(geo_results):
|
|
|
|
|
|
if 'geo_scores' not in result:
|
|
|
validation_result['warnings'].append(f"Result {i} missing geo_scores")
|
|
|
|
|
|
if 'page_data' not in result:
|
|
|
validation_result['warnings'].append(f"Result {i} missing page_data")
|
|
|
|
|
|
|
|
|
if 'geo_scores' in result:
|
|
|
for metric, score in result['geo_scores'].items():
|
|
|
if not isinstance(score, (int, float)) or score < 0 or score > 10:
|
|
|
validation_result['errors'].append(f"Invalid score for {metric} in result {i}")
|
|
|
validation_result['valid'] = False
|
|
|
|
|
|
return validation_result
|
|
|
|
|
|
except Exception as e:
|
|
|
validation_result['errors'].append(f"Validation failed: {str(e)}")
|
|
|
validation_result['valid'] = False
|
|
|
return validation_result
|
|
|
|
|
|
@staticmethod
|
|
|
def validate_enhancement_data(enhancement_result: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
"""Validate content enhancement data structure"""
|
|
|
validation_result = {
|
|
|
'valid': True,
|
|
|
'errors': [],
|
|
|
'warnings': []
|
|
|
}
|
|
|
|
|
|
try:
|
|
|
|
|
|
if 'scores' not in enhancement_result:
|
|
|
validation_result['warnings'].append("Enhancement result missing scores")
|
|
|
|
|
|
|
|
|
if 'scores' in enhancement_result:
|
|
|
scores = enhancement_result['scores']
|
|
|
required_scores = ['clarity', 'structuredness', 'answerability']
|
|
|
|
|
|
for req_score in required_scores:
|
|
|
if req_score not in scores:
|
|
|
validation_result['warnings'].append(f"Missing {req_score} score")
|
|
|
elif not isinstance(scores[req_score], (int, float)):
|
|
|
validation_result['errors'].append(f"Invalid {req_score} score type")
|
|
|
validation_result['valid'] = False
|
|
|
|
|
|
return validation_result
|
|
|
|
|
|
except Exception as e:
|
|
|
validation_result['errors'].append(f"Enhancement validation failed: {str(e)}")
|
|
|
validation_result['valid'] = False
|
|
|
return validation_result
|
|
|
|
|
|
|
|
|
class ExportManager:
|
|
|
"""High-level export management class"""
|
|
|
|
|
|
def __init__(self):
|
|
|
self.exporter = ResultExporter()
|
|
|
self.validator = DataValidator()
|
|
|
self.export_history = []
|
|
|
|
|
|
def export_with_validation(self, data: Dict[str, Any], data_type: str,
|
|
|
format_type: str = 'json') -> Dict[str, Any]:
|
|
|
"""Export data with validation"""
|
|
|
try:
|
|
|
|
|
|
if data_type == 'geo_analysis':
|
|
|
validation = self.validator.validate_geo_data(data.get('geo_results', []))
|
|
|
elif data_type == 'content_optimization':
|
|
|
validation = self.validator.validate_enhancement_data(data)
|
|
|
else:
|
|
|
validation = {'valid': True, 'errors': [], 'warnings': []}
|
|
|
|
|
|
|
|
|
if validation['valid']:
|
|
|
if data_type == 'geo_analysis':
|
|
|
result = self.exporter.export_geo_results(
|
|
|
data.get('geo_results', []),
|
|
|
data.get('website_url', 'unknown'),
|
|
|
format_type
|
|
|
)
|
|
|
elif data_type == 'content_optimization':
|
|
|
result = self.exporter.export_enhancement_results(data, format_type)
|
|
|
else:
|
|
|
result = json.dumps(data, indent=2, ensure_ascii=False)
|
|
|
|
|
|
|
|
|
self.export_history.append({
|
|
|
'timestamp': datetime.now().isoformat(),
|
|
|
'data_type': data_type,
|
|
|
'format_type': format_type,
|
|
|
'validation_warnings': validation.get('warnings', []),
|
|
|
'success': True
|
|
|
})
|
|
|
|
|
|
return {
|
|
|
'success': True,
|
|
|
'data': result,
|
|
|
'validation': validation
|
|
|
}
|
|
|
else:
|
|
|
return {
|
|
|
'success': False,
|
|
|
'error': 'Data validation failed',
|
|
|
'validation': validation
|
|
|
}
|
|
|
|
|
|
except Exception as e:
|
|
|
self.export_history.append({
|
|
|
'timestamp': datetime.now().isoformat(),
|
|
|
'data_type': data_type,
|
|
|
'format_type': format_type,
|
|
|
'success': False,
|
|
|
'error': str(e)
|
|
|
})
|
|
|
|
|
|
return {
|
|
|
'success': False,
|
|
|
'error': f"Export failed: {str(e)}"
|
|
|
}
|
|
|
|
|
|
def get_export_history(self) -> List[Dict[str, Any]]:
|
|
|
"""Get export history"""
|
|
|
return self.export_history
|
|
|
|
|
|
def clear_export_history(self) -> None:
|
|
|
"""Clear export history"""
|
|
|
self.export_history.clear()
|
|
|
|
|
|
def get_supported_formats(self) -> Dict[str, List[str]]:
|
|
|
"""Get supported export formats by data type"""
|
|
|
return {
|
|
|
'geo_analysis': ['json', 'csv', 'html', 'xlsx', 'pdf'],
|
|
|
'content_optimization': ['json', 'html', 'csv'],
|
|
|
'qa_results': ['json', 'html', 'csv'],
|
|
|
'batch_analysis': ['json', 'xlsx', 'csv']
|
|
|
}
|
|
|
|
|
|
def create_multi_format_export(self, data: Dict[str, Any], data_type: str,
|
|
|
formats: List[str] = None) -> Dict[str, Any]:
|
|
|
"""Create export in multiple formats"""
|
|
|
if formats is None:
|
|
|
formats = ['json', 'html', 'csv']
|
|
|
|
|
|
results = {}
|
|
|
|
|
|
for format_type in formats:
|
|
|
try:
|
|
|
export_result = self.export_with_validation(data, data_type, format_type)
|
|
|
if export_result['success']:
|
|
|
results[format_type] = export_result['data']
|
|
|
else:
|
|
|
results[format_type] = {'error': export_result['error']}
|
|
|
|
|
|
except Exception as e:
|
|
|
results[format_type] = {'error': str(e)}
|
|
|
|
|
|
return {
|
|
|
'multi_format_export': results,
|
|
|
'formats_generated': list(results.keys()),
|
|
|
'successful_formats': [fmt for fmt, data in results.items() if 'error' not in data]
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_export_template(data_type: str) -> Dict[str, Any]:
|
|
|
"""Create export template for different data types"""
|
|
|
templates = {
|
|
|
'geo_analysis': {
|
|
|
'website_url': 'https://example.com',
|
|
|
'geo_results': [
|
|
|
{
|
|
|
'page_data': {
|
|
|
'url': 'https://example.com/page1',
|
|
|
'title': 'Example Page',
|
|
|
'word_count': 500
|
|
|
},
|
|
|
'geo_scores': {
|
|
|
'ai_search_visibility': 7.5,
|
|
|
'query_intent_matching': 6.8,
|
|
|
'conversational_readiness': 8.2,
|
|
|
'citation_worthiness': 7.1
|
|
|
},
|
|
|
'recommendations': [
|
|
|
'Improve content structure',
|
|
|
'Add more specific examples'
|
|
|
]
|
|
|
}
|
|
|
]
|
|
|
},
|
|
|
'content_optimization': {
|
|
|
'scores': {
|
|
|
'clarity': 7.5,
|
|
|
'structuredness': 6.8,
|
|
|
'answerability': 8.2
|
|
|
},
|
|
|
'keywords': ['example', 'optimization', 'content'],
|
|
|
'optimized_text': 'This is the optimized version of the content...',
|
|
|
'optimization_suggestions': [
|
|
|
'Improve sentence structure',
|
|
|
'Add more specific keywords'
|
|
|
]
|
|
|
},
|
|
|
'qa_results': [
|
|
|
{
|
|
|
'query': 'What is the main topic?',
|
|
|
'result': 'The main topic is content optimization for AI systems.',
|
|
|
'sources': [
|
|
|
{
|
|
|
'content': 'Source document content...',
|
|
|
'metadata': {'source': 'document1.pdf'}
|
|
|
}
|
|
|
]
|
|
|
}
|
|
|
]
|
|
|
}
|
|
|
|
|
|
return templates.get(data_type, {})
|
|
|
|
|
|
|
|
|
def export_demo_data() -> Dict[str, Any]:
|
|
|
"""Export demonstration data for testing"""
|
|
|
demo_data = {
|
|
|
'geo_analysis_demo': create_export_template('geo_analysis'),
|
|
|
'content_optimization_demo': create_export_template('content_optimization'),
|
|
|
'qa_results_demo': create_export_template('qa_results')
|
|
|
}
|
|
|
|
|
|
return demo_data
|
|
|
|
|
|
|
|
|
|
|
|
__all__ = [
|
|
|
'ResultExporter',
|
|
|
'GEOReport',
|
|
|
'ContentAnalysis',
|
|
|
'DataValidator',
|
|
|
'ExportManager',
|
|
|
'create_export_template',
|
|
|
'export_demo_data'
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
exporter = ResultExporter()
|
|
|
|
|
|
|
|
|
demo_geo_data = create_export_template('geo_analysis')
|
|
|
|
|
|
|
|
|
json_export = exporter.export_geo_results(
|
|
|
demo_geo_data['geo_results'],
|
|
|
demo_geo_data['website_url'],
|
|
|
'json'
|
|
|
)
|
|
|
|
|
|
html_export = exporter.export_geo_results(
|
|
|
demo_geo_data['geo_results'],
|
|
|
demo_geo_data['website_url'],
|
|
|
'html'
|
|
|
)
|
|
|
|
|
|
print("JSON Export:", json_export[:200] + "..." if len(str(json_export)) > 200 else json_export)
|
|
|
print("\nHTML Export:", html_export[:200] + "..." if len(str(html_export)) > 200 else html_export)
|
|
|
|
|
|
|
|
|
demo_enhancement = create_export_template('content_optimization')
|
|
|
enhancement_export = exporter.export_enhancement_results(demo_enhancement, 'json')
|
|
|
|
|
|
print("\nEnhancement Export:", enhancement_export[:200] + "..." if len(str(enhancement_export)) > 200 else enhancement_export) |