Alpha108/GenerativeEngineOptimization
0
1"""
2Results Export and Reporting Module
3Handles export of analysis results, reports, and data for external use
4"""
5
6import json
7import csv
8import io
9import zipfile
10import tempfile
11import os
12from datetime import datetime
13from typing import Dict, Any, List, Optional, Union
14import pandas as pd
15from dataclasses import dataclass, asdict
16
17
18@dataclass
19class GEOReport:
20 """Data class for GEO analysis reports"""
21 website_url: str
22 analysis_date: str
23 overall_score: float
24 pages_analyzed: int
25 geo_scores: Dict[str, float]
26 recommendations: List[str]
27 optimization_opportunities: List[Dict[str, Any]]
28 competitive_position: str
29
30 def to_dict(self) -> Dict[str, Any]:
31 """Convert report to dictionary"""
32 return asdict(self)
33
34
35@dataclass
36class ContentAnalysis:
37 """Data class for content optimization analysis"""
38 original_content: str
39 analysis_date: str
40 clarity_score: float
41 structure_score: float
42 answerability_score: float
43 keywords: List[str]
44 optimized_content: Optional[str]
45 improvements_made: List[str]
46
47 def to_dict(self) -> Dict[str, Any]:
48 """Convert analysis to dictionary"""
49 return asdict(self)
50
51
52class ResultExporter:
53 """Main class for exporting analysis results and generating reports"""
54
55 def __init__(self):
56 self.export_formats = ['json', 'csv', 'html', 'pdf', 'xlsx']
57 self.supported_types = ['geo_analysis', 'content_optimization', 'qa_results', 'batch_analysis']
58
59 def export_geo_results(self, geo_results: List[Dict[str, Any]],
60 website_url: str, format_type: str = 'json') -> Union[str, bytes, Dict[str, Any]]:
61 """
62 Export GEO analysis results in specified format
63
64 Args:
65 geo_results (List[Dict]): List of GEO analysis results
66 website_url (str): URL of analyzed website
67 format_type (str): Export format ('json', 'csv', 'html', 'xlsx')
68
69 Returns:
70 Union[str, bytes, Dict]: Exported data in requested format
71 """
72 try:
73 # Prepare consolidated data
74 export_data = self._prepare_geo_export_data(geo_results, website_url)
75
76 if format_type.lower() == 'json':
77 return self._export_geo_json(export_data)
78 elif format_type.lower() == 'csv':
79 return self._export_geo_csv(export_data)
80 elif format_type.lower() == 'html':
81 return self._export_geo_html(export_data)
82 elif format_type.lower() == 'xlsx':
83 return self._export_geo_excel(export_data)
84 elif format_type.lower() == 'pdf':
85 return self._export_geo_pdf(export_data)
86 else:
87 raise ValueError(f"Unsupported export format: {format_type}")
88
89 except Exception as e:
90 return {'error': f"Export failed: {str(e)}"}
91
92 def export_enhancement_results(self, enhancement_result: Dict[str, Any],
93 format_type: str = 'json') -> Union[str, bytes, Dict[str, Any]]:
94 """
95 Export content enhancement results
96
97 Args:
98 enhancement_result (Dict): Content enhancement analysis result
99 format_type (str): Export format
100
101 Returns:
102 Union[str, bytes, Dict]: Exported data
103 """
104 try:
105 # Prepare data for export
106 export_data = self._prepare_enhancement_export_data(enhancement_result)
107
108 if format_type.lower() == 'json':
109 return json.dumps(export_data, indent=2, ensure_ascii=False)
110 elif format_type.lower() == 'html':
111 return self._export_enhancement_html(export_data)
112 elif format_type.lower() == 'csv':
113 return self._export_enhancement_csv(export_data)
114 else:
115 return json.dumps(export_data, indent=2, ensure_ascii=False)
116
117 except Exception as e:
118 return {'error': f"Enhancement export failed: {str(e)}"}
119
120 def export_qa_results(self, qa_results: List[Dict[str, Any]],
121 format_type: str = 'json') -> Union[str, bytes, Dict[str, Any]]:
122 """
123 Export Q&A session results
124
125 Args:
126 qa_results (List[Dict]): List of Q&A interactions
127 format_type (str): Export format
128
129 Returns:
130 Union[str, bytes, Dict]: Exported data
131 """
132 try:
133 export_data = {
134 'qa_session': {
135 'session_date': datetime.now().isoformat(),
136 'total_questions': len(qa_results),
137 'interactions': qa_results
138 },
139 'summary': {
140 'successful_answers': len([r for r in qa_results if not r.get('error')]),
141 'average_response_length': self._calculate_avg_response_length(qa_results),
142 'most_common_topics': self._extract_common_topics(qa_results)
143 }
144 }
145
146 if format_type.lower() == 'json':
147 return json.dumps(export_data, indent=2, ensure_ascii=False)
148 elif format_type.lower() == 'html':
149 return self._export_qa_html(export_data)
150 elif format_type.lower() == 'csv':
151 return self._export_qa_csv(export_data)
152 else:
153 return json.dumps(export_data, indent=2, ensure_ascii=False)
154
155 except Exception as e:
156 return {'error': f"Q&A export failed: {str(e)}"}
157
158 def create_comprehensive_report(self, analysis_data: Dict[str, Any],
159 report_type: str = 'full') -> Dict[str, Any]:
160 """
161 Create comprehensive analysis report
162
163 Args:
164 analysis_data (Dict): Combined analysis data from multiple sources
165 report_type (str): Type of report ('full', 'summary', 'executive')
166
167 Returns:
168 Dict: Comprehensive report data
169 """
170 try:
171 report = {
172 'report_metadata': {
173 'generated_at': datetime.now().isoformat(),
174 'report_type': report_type,
175 'generator': 'GEO SEO AI Optimizer',
176 'version': '1.0'
177 }
178 }
179
180 if report_type == 'executive':
181 report.update(self._create_executive_summary(analysis_data))
182 elif report_type == 'summary':
183 report.update(self._create_summary_report(analysis_data))
184 else: # full report
185 report.update(self._create_full_report(analysis_data))
186
187 return report
188
189 except Exception as e:
190 return {'error': f"Report creation failed: {str(e)}"}
191
192 def export_batch_results(self, batch_results: List[Dict[str, Any]],
193 batch_metadata: Dict[str, Any],
194 format_type: str = 'xlsx') -> Union[str, bytes, Dict[str, Any]]:
195 """
196 Export batch analysis results
197
198 Args:
199 batch_results (List[Dict]): List of batch analysis results
200 batch_metadata (Dict): Metadata about the batch process
201 format_type (str): Export format
202
203 Returns:
204 Union[str, bytes, Dict]: Exported batch data
205 """
206 try:
207 export_data = {
208 'batch_metadata': batch_metadata,
209 'batch_results': batch_results,
210 'batch_summary': self._create_batch_summary(batch_results),
211 'export_timestamp': datetime.now().isoformat()
212 }
213
214 if format_type.lower() == 'xlsx':
215 return self._export_batch_excel(export_data)
216 elif format_type.lower() == 'json':
217 return json.dumps(export_data, indent=2, ensure_ascii=False)
218 elif format_type.lower() == 'csv':
219 return self._export_batch_csv(export_data)
220 else:
221 return json.dumps(export_data, indent=2, ensure_ascii=False)
222
223 except Exception as e:
224 return {'error': f"Batch export failed: {str(e)}"}
225
226 def create_export_package(self, analysis_data: Dict[str, Any],
227 package_name: str = "geo_analysis") -> bytes:
228 """
229 Create a ZIP package with multiple export formats
230
231 Args:
232 analysis_data (Dict): Analysis data to package
233 package_name (str): Name for the package
234
235 Returns:
236 bytes: ZIP file content
237 """
238 try:
239 # Create temporary directory
240 with tempfile.TemporaryDirectory() as temp_dir:
241 zip_path = os.path.join(temp_dir, f"{package_name}.zip")
242
243 with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zip_file:
244 # Add JSON export
245 json_data = json.dumps(analysis_data, indent=2, ensure_ascii=False)
246 zip_file.writestr(f"{package_name}.json", json_data)
247
248 # Add HTML report
249 if 'geo_results' in analysis_data:
250 html_data = self._export_geo_html(analysis_data)
251 zip_file.writestr(f"{package_name}_report.html", html_data)
252
253 # Add CSV data
254 if 'geo_results' in analysis_data:
255 csv_data = self._export_geo_csv(analysis_data)
256 zip_file.writestr(f"{package_name}_data.csv", csv_data)
257
258 # Add README
259 readme_content = self._generate_package_readme(analysis_data)
260 zip_file.writestr("README.txt", readme_content)
261
262 # Read the ZIP file
263 with open(zip_path, 'rb') as zip_file:
264 return zip_file.read()
265
266 except Exception as e:
267 raise Exception(f"Package creation failed: {str(e)}")
268
269 def _prepare_geo_export_data(self, geo_results: List[Dict[str, Any]], website_url: str) -> Dict[str, Any]:
270 """Prepare GEO data for export"""
271 try:
272 # Calculate aggregate metrics
273 valid_results = [r for r in geo_results if 'geo_scores' in r and not r.get('error')]
274
275 if not valid_results:
276 return {
277 'error': 'No valid GEO results to export',
278 'website_url': website_url,
279 'export_timestamp': datetime.now().isoformat()
280 }
281
282 # Aggregate scores
283 all_scores = {}
284 for result in valid_results:
285 for metric, score in result.get('geo_scores', {}).items():
286 if metric not in all_scores:
287 all_scores[metric] = []
288 all_scores[metric].append(score)
289
290 avg_scores = {metric: sum(scores) / len(scores) for metric, scores in all_scores.items()}
291 overall_avg = sum(avg_scores.values()) / len(avg_scores) if avg_scores else 0
292
293 # Collect recommendations
294 all_recommendations = []
295 all_opportunities = []
296
297 for result in valid_results:
298 all_recommendations.extend(result.get('recommendations', []))
299 all_opportunities.extend(result.get('optimization_opportunities', []))
300
301 # Remove duplicates
302 unique_recommendations = list(set(all_recommendations))
303
304 return {
305 'website_analysis': {
306 'url': website_url,
307 'analysis_date': datetime.now().isoformat(),
308 'pages_analyzed': len(valid_results),
309 'overall_geo_score': round(overall_avg, 2)
310 },
311 'aggregate_scores': avg_scores,
312 'individual_page_results': valid_results,
313 'recommendations': unique_recommendations[:10], # Top 10
314 'optimization_opportunities': all_opportunities,
315 'performance_insights': self._generate_performance_insights(avg_scores, overall_avg),
316 'export_metadata': {
317 'exported_by': 'GEO SEO AI Optimizer',
318 'export_timestamp': datetime.now().isoformat(),
319 'data_format': 'GEO Analysis Results v1.0'
320 }
321 }
322
323 except Exception as e:
324 return {'error': f"Data preparation failed: {str(e)}"}
325
326 def _prepare_enhancement_export_data(self, enhancement_result: Dict[str, Any]) -> Dict[str, Any]:
327 """Prepare content enhancement data for export"""
328 try:
329 scores = enhancement_result.get('scores', {})
330
331 return {
332 'content_analysis': {
333 'analysis_date': datetime.now().isoformat(),
334 'original_content_length': enhancement_result.get('original_length', 0),
335 'original_word_count': enhancement_result.get('original_word_count', 0),
336 'analysis_type': enhancement_result.get('optimization_type', 'standard')
337 },
338 'performance_scores': {
339 'clarity': scores.get('clarity', 0),
340 'structure': scores.get('structuredness', 0),
341 'answerability': scores.get('answerability', 0),
342 'overall_average': sum(scores.values()) / len(scores) if scores else 0
343 },
344 'optimization_results': {
345 'keywords_identified': enhancement_result.get('keywords', []),
346 'optimized_content': enhancement_result.get('optimized_text', ''),
347 'improvements_made': enhancement_result.get('optimization_suggestions', []),
348 'analyze_only': enhancement_result.get('analyze_only', False)
349 },
350 'export_metadata': {
351 'exported_by': 'GEO SEO AI Optimizer',
352 'export_timestamp': datetime.now().isoformat(),
353 'data_format': 'Content Enhancement Results v1.0'
354 }
355 }
356
357 except Exception as e:
358 return {'error': f"Enhancement data preparation failed: {str(e)}"}
359
360 def _export_geo_json(self, data: Dict[str, Any]) -> str:
361 """Export GEO data as JSON"""
362 return json.dumps(data, indent=2, ensure_ascii=False)
363
364 def _export_geo_csv(self, data: Dict[str, Any]) -> str:
365 """Export GEO data as CSV"""
366 try:
367 output = io.StringIO()
368
369 # Write aggregate scores
370 writer = csv.writer(output)
371 writer.writerow(['GEO Analysis Results'])
372 writer.writerow(['Website:', data.get('website_analysis', {}).get('url', 'Unknown')])
373 writer.writerow(['Analysis Date:', data.get('website_analysis', {}).get('analysis_date', 'Unknown')])
374 writer.writerow(['Overall Score:', data.get('website_analysis', {}).get('overall_geo_score', 0)])
375 writer.writerow([])
376
377 # Write aggregate scores
378 writer.writerow(['Metric', 'Score'])
379 for metric, score in data.get('aggregate_scores', {}).items():
380 writer.writerow([metric.replace('_', ' ').title(), round(score, 2)])
381
382 writer.writerow([])
383 writer.writerow(['Recommendations'])
384 for i, rec in enumerate(data.get('recommendations', []), 1):
385 writer.writerow([f"{i}.", rec])
386
387 # Individual page results
388 if data.get('individual_page_results'):
389 writer.writerow([])
390 writer.writerow(['Individual Page Results'])
391
392 # Header for page results
393 first_result = data['individual_page_results'][0]
394 if 'geo_scores' in first_result:
395 headers = ['Page Index', 'Page URL', 'Page Title'] + list(first_result['geo_scores'].keys())
396 writer.writerow(headers)
397
398 for i, result in enumerate(data['individual_page_results']):
399 page_data = result.get('page_data', {})
400 scores = result.get('geo_scores', {})
401
402 row = [
403 i + 1,
404 page_data.get('url', 'Unknown'),
405 page_data.get('title', 'Unknown')
406 ] + [round(scores.get(metric, 0), 2) for metric in headers[3:]]
407
408 writer.writerow(row)
409
410 return output.getvalue()
411
412 except Exception as e:
413 return f"CSV export error: {str(e)}"
414
415 def _export_geo_html(self, data: Dict[str, Any]) -> str:
416 """Export GEO data as HTML report"""
417 try:
418 website_info = data.get('website_analysis', {})
419 scores = data.get('aggregate_scores', {})
420 recommendations = data.get('recommendations', [])
421
422 html_content = f"""
423<!DOCTYPE html>
424<html lang="en">
425<head>
426 <meta charset="UTF-8">
427 <meta name="viewport" content="width=device-width, initial-scale=1.0">
428 <title>GEO Analysis Report - {website_info.get('url', 'Website')}</title>
429 <style>
430 body {{
431 font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
432 line-height: 1.6;
433 color: #333;
434 max-width: 1200px;
435 margin: 0 auto;
436 padding: 20px;
437 background-color: #f5f5f5;
438 }}
439 .header {{
440 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
441 color: white;
442 padding: 30px;
443 border-radius: 10px;
444 margin-bottom: 30px;
445 text-align: center;
446 }}
447 .header h1 {{
448 margin: 0;
449 font-size: 2.5em;
450 }}
451 .summary-cards {{
452 display: grid;
453 grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
454 gap: 20px;
455 margin-bottom: 30px;
456 }}
457 .card {{
458 background: white;
459 padding: 20px;
460 border-radius: 10px;
461 box-shadow: 0 4px 6px rgba(0,0,0,0.1);
462 text-align: center;
463 }}
464 .card h3 {{
465 margin-top: 0;
466 color: #667eea;
467 }}
468 .score {{
469 font-size: 2em;
470 font-weight: bold;
471 color: #333;
472 }}
473 .scores-grid {{
474 display: grid;
475 grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
476 gap: 20px;
477 margin-bottom: 30px;
478 }}
479 .score-item {{
480 background: white;
481 padding: 15px;
482 border-radius: 8px;
483 box-shadow: 0 2px 4px rgba(0,0,0,0.1);
484 display: flex;
485 justify-content: space-between;
486 align-items: center;
487 }}
488 .score-bar {{
489 width: 100px;
490 height: 10px;
491 background: #e0e0e0;
492 border-radius: 5px;
493 overflow: hidden;
494 }}
495 .score-fill {{
496 height: 100%;
497 background: linear-gradient(90deg, #ff6b6b, #ffa500, #4ecdc4);
498 transition: width 0.3s ease;
499 }}
500 .recommendations {{
501 background: white;
502 padding: 30px;
503 border-radius: 10px;
504 box-shadow: 0 4px 6px rgba(0,0,0,0.1);
505 margin-bottom: 30px;
506 }}
507 .recommendations h2 {{
508 color: #667eea;
509 border-bottom: 2px solid #667eea;
510 padding-bottom: 10px;
511 }}
512 .rec-item {{
513 padding: 10px 0;
514 border-bottom: 1px solid #eee;
515 }}
516 .footer {{
517 text-align: center;
518 color: #666;
519 margin-top: 40px;
520 padding-top: 20px;
521 border-top: 1px solid #ddd;
522 }}
523 </style>
524</head>
525<body>
526 <div class="header">
527 <h1>🚀 GEO Analysis Report</h1>
528 <p>Generative Engine Optimization Performance Analysis</p>
529 <p><strong>Website:</strong> {website_info.get('url', 'Not specified')}</p>
530 <p><strong>Analysis Date:</strong> {website_info.get('analysis_date', 'Not specified')}</p>
531 </div>
532
533 <div class="summary-cards">
534 <div class="card">
535 <h3>Overall GEO Score</h3>
536 <div class="score">{website_info.get('overall_geo_score', 0)}/10</div>
537 </div>
538 <div class="card">
539 <h3>Pages Analyzed</h3>
540 <div class="score">{website_info.get('pages_analyzed', 0)}</div>
541 </div>
542 <div class="card">
543 <h3>Recommendations</h3>
544 <div class="score">{len(recommendations)}</div>
545 </div>
546 </div>
547
548 <h2>📊 Detailed GEO Metrics</h2>
549 <div class="scores-grid">
550 """
551
552 # Add individual scores
553 for metric, score in scores.items():
554 metric_display = metric.replace('_', ' ').title()
555 score_percentage = min(score * 10, 100) # Convert to percentage
556
557 html_content += f"""
558 <div class="score-item">
559 <div>
560 <strong>{metric_display}</strong><br>
561 <span style="color: #666;">{score:.1f}/10</span>
562 </div>
563 <div class="score-bar">
564 <div class="score-fill" style="width: {score_percentage}%;"></div>
565 </div>
566 </div>
567 """
568
569 html_content += """
570 </div>
571
572 <div class="recommendations">
573 <h2>💡 Optimization Recommendations</h2>
574 """
575
576 # Add recommendations
577 for i, rec in enumerate(recommendations, 1):
578 html_content += f'<div class="rec-item"><strong>{i}.</strong> {rec}</div>'
579
580 html_content += f"""
581 </div>
582
583 <div class="footer">
584 <p>Generated by GEO SEO AI Optimizer | {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
585 <p>This report provides AI-first SEO optimization insights for better generative engine performance.</p>
586 </div>
587</body>
588</html>
589 """
590
591 return html_content
592
593 except Exception as e:
594 return f"<html><body><h1>HTML Export Error</h1><p>{str(e)}</p></body></html>"
595
596 def _export_geo_excel(self, data: Dict[str, Any]) -> bytes:
597 """Export GEO data as Excel file"""
598 try:
599 output = io.BytesIO()
600
601 with pd.ExcelWriter(output, engine='openpyxl') as writer:
602 # Summary sheet
603 summary_data = {
604 'Metric': ['Website URL', 'Analysis Date', 'Pages Analyzed', 'Overall Score'],
605 'Value': [
606 data.get('website_analysis', {}).get('url', 'Unknown'),
607 data.get('website_analysis', {}).get('analysis_date', 'Unknown'),
608 data.get('website_analysis', {}).get('pages_analyzed', 0),
609 data.get('website_analysis', {}).get('overall_geo_score', 0)
610 ]
611 }
612 pd.DataFrame(summary_data).to_excel(writer, sheet_name='Summary', index=False)
613
614 # Scores sheet
615 scores_data = []
616 for metric, score in data.get('aggregate_scores', {}).items():
617 scores_data.append({
618 'Metric': metric.replace('_', ' ').title(),
619 'Score': round(score, 2),
620 'Performance': self._get_performance_level(score)
621 })
622
623 pd.DataFrame(scores_data).to_excel(writer, sheet_name='GEO Scores', index=False)
624
625 # Recommendations sheet
626 rec_data = []
627 for i, rec in enumerate(data.get('recommendations', []), 1):
628 rec_data.append({
629 'Priority': i,
630 'Recommendation': rec,
631 'Category': self._categorize_recommendation(rec)
632 })
633
634 if rec_data:
635 pd.DataFrame(rec_data).to_excel(writer, sheet_name='Recommendations', index=False)
636
637 # Individual pages sheet
638 if data.get('individual_page_results'):
639 pages_data = []
640 for i, result in enumerate(data['individual_page_results']):
641 page_data = result.get('page_data', {})
642 scores = result.get('geo_scores', {})
643
644 page_row = {
645 'Page_Index': i + 1,
646 'URL': page_data.get('url', 'Unknown'),
647 'Title': page_data.get('title', 'Unknown'),
648 'Word_Count': page_data.get('word_count', 0)
649 }
650
651 # Add all GEO scores
652 for metric, score in scores.items():
653 page_row[metric.replace('_', ' ').title()] = round(score, 2)
654
655 pages_data.append(page_row)
656
657 pd.DataFrame(pages_data).to_excel(writer, sheet_name='Individual Pages', index=False)
658
659 output.seek(0)
660 return output.getvalue()
661
662 except Exception as e:
663 # Return error as text file if Excel creation fails
664 error_content = f"Excel export failed: {str(e)}\n\nData:\n{json.dumps(data, indent=2)}"
665 return error_content.encode('utf-8')
666
667 def _export_enhancement_html(self, data: Dict[str, Any]) -> str:
668 """Export content enhancement results as HTML"""
669 try:
670 analysis = data.get('content_analysis', {})
671 scores = data.get('performance_scores', {})
672 optimization = data.get('optimization_results', {})
673
674 html_content = f"""
675<!DOCTYPE html>
676<html lang="en">
677<head>
678 <meta charset="UTF-8">
679 <meta name="viewport" content="width=device-width, initial-scale=1.0">
680 <title>Content Enhancement Report</title>
681 <style>
682 body {{
683 font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
684 line-height: 1.6;
685 color: #333;
686 max-width: 1000px;
687 margin: 0 auto;
688 padding: 20px;
689 background-color: #f8f9fa;
690 }}
691 .header {{
692 background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
693 color: white;
694 padding: 30px;
695 border-radius: 10px;
696 margin-bottom: 30px;
697 text-align: center;
698 }}
699 .scores {{
700 display: grid;
701 grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
702 gap: 20px;
703 margin-bottom: 30px;
704 }}
705 .score-card {{
706 background: white;
707 padding: 20px;
708 border-radius: 10px;
709 box-shadow: 0 4px 6px rgba(0,0,0,0.1);
710 text-align: center;
711 }}
712 .content-section {{
713 background: white;
714 padding: 30px;
715 border-radius: 10px;
716 box-shadow: 0 4px 6px rgba(0,0,0,0.1);
717 margin-bottom: 20px;
718 }}
719 .keywords {{
720 display: flex;
721 flex-wrap: wrap;
722 gap: 10px;
723 margin-top: 15px;
724 }}
725 .keyword {{
726 background: #e9ecef;
727 padding: 5px 10px;
728 border-radius: 20px;
729 font-size: 0.9em;
730 }}
731 .optimized-content {{
732 background: #f8f9fa;
733 padding: 20px;
734 border-left: 4px solid #28a745;
735 border-radius: 5px;
736 font-style: italic;
737 }}
738 </style>
739</head>
740<body>
741 <div class="header">
742 <h1>🔧 Content Enhancement Report</h1>
743 <p>AI-Optimized Content Analysis Results</p>
744 <p><strong>Analysis Date:</strong> {analysis.get('analysis_date', 'Unknown')}</p>
745 </div>
746
747 <div class="scores">
748 <div class="score-card">
749 <h3>Clarity Score</h3>
750 <div style="font-size: 2em; font-weight: bold; color: #28a745;">
751 {scores.get('clarity', 0):.1f}/10
752 </div>
753 </div>
754 <div class="score-card">
755 <h3>Structure Score</h3>
756 <div style="font-size: 2em; font-weight: bold; color: #28a745;">
757 {scores.get('structure', 0):.1f}/10
758 </div>
759 </div>
760 <div class="score-card">
761 <h3>Answerability Score</h3>
762 <div style="font-size: 2em; font-weight: bold; color: #28a745;">
763 {scores.get('answerability', 0):.1f}/10
764 </div>
765 </div>
766 <div class="score-card">
767 <h3>Overall Average</h3>
768 <div style="font-size: 2em; font-weight: bold; color: #28a745;">
769 {scores.get('overall_average', 0):.1f}/10
770 </div>
771 </div>
772 </div>
773
774 <div class="content-section">
775 <h2>🔑 Identified Keywords</h2>
776 <div class="keywords">
777 {' '.join([f'<span class="keyword">{keyword}</span>' for keyword in optimization.get('keywords_identified', [])])}
778 </div>
779 </div>
780
781 {'<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 ''}
782
783 <div class="content-section">
784 <h2>💡 Improvements Made</h2>
785 <ul>
786 {' '.join([f'<li>{improvement}</li>' for improvement in optimization.get('improvements_made', [])])}
787 </ul>
788 </div>
789
790 <div style="text-align: center; color: #666; margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd;">
791 <p>Generated by GEO SEO AI Optimizer | {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
792 </div>
793</body>
794</html>
795 """
796
797 return html_content
798
799 except Exception as e:
800 return f"<html><body><h1>Enhancement HTML Export Error</h1><p>{str(e)}</p></body></html>"
801
802 def _export_enhancement_csv(self, data: Dict[str, Any]) -> str:
803 """Export content enhancement results as CSV"""
804 try:
805 output = io.StringIO()
806 writer = csv.writer(output)
807
808 # Header information
809 analysis = data.get('content_analysis', {})
810 scores = data.get('performance_scores', {})
811 optimization = data.get('optimization_results', {})
812
813 writer.writerow(['Content Enhancement Analysis Report'])
814 writer.writerow(['Analysis Date:', analysis.get('analysis_date', 'Unknown')])
815 writer.writerow(['Original Content Length:', analysis.get('original_content_length', 0)])
816 writer.writerow(['Original Word Count:', analysis.get('original_word_count', 0)])
817 writer.writerow([])
818
819 # Performance scores
820 writer.writerow(['Performance Scores'])
821 writer.writerow(['Metric', 'Score'])
822 for metric, score in scores.items():
823 writer.writerow([metric.replace('_', ' ').title(), round(score, 2)])
824
825 writer.writerow([])
826 writer.writerow(['Keywords Identified'])
827 for keyword in optimization.get('keywords_identified', []):
828 writer.writerow([keyword])
829
830 writer.writerow([])
831 writer.writerow(['Improvements Made'])
832 for improvement in optimization.get('improvements_made', []):
833 writer.writerow([improvement])
834
835 return output.getvalue()
836
837 except Exception as e:
838 return f"Enhancement CSV export error: {str(e)}"
839
840 def _export_qa_html(self, data: Dict[str, Any]) -> str:
841 """Export Q&A results as HTML"""
842 try:
843 session = data.get('qa_session', {})
844 summary = data.get('summary', {})
845 interactions = session.get('interactions', [])
846
847 html_content = f"""
848<!DOCTYPE html>
849<html lang="en">
850<head>
851 <meta charset="UTF-8">
852 <meta name="viewport" content="width=device-width, initial-scale=1.0">
853 <title>Q&A Session Report</title>
854 <style>
855 body {{
856 font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
857 line-height: 1.6;
858 color: #333;
859 max-width: 1000px;
860 margin: 0 auto;
861 padding: 20px;
862 background-color: #f8f9fa;
863 }}
864 .header {{
865 background: linear-gradient(135deg, #6f42c1 0%, #e83e8c 100%);
866 color: white;
867 padding: 30px;
868 border-radius: 10px;
869 margin-bottom: 30px;
870 text-align: center;
871 }}
872 .summary {{
873 display: grid;
874 grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
875 gap: 20px;
876 margin-bottom: 30px;
877 }}
878 .summary-card {{
879 background: white;
880 padding: 20px;
881 border-radius: 10px;
882 box-shadow: 0 4px 6px rgba(0,0,0,0.1);
883 text-align: center;
884 }}
885 .qa-item {{
886 background: white;
887 padding: 20px;
888 border-radius: 10px;
889 box-shadow: 0 4px 6px rgba(0,0,0,0.1);
890 margin-bottom: 20px;
891 }}
892 .question {{
893 background: #e9ecef;
894 padding: 15px;
895 border-left: 4px solid #6f42c1;
896 border-radius: 5px;
897 margin-bottom: 15px;
898 }}
899 .answer {{
900 padding: 15px;
901 border-left: 4px solid #28a745;
902 border-radius: 5px;
903 background: #f8f9fa;
904 }}
905 .sources {{
906 margin-top: 15px;
907 padding: 10px;
908 background: #fff3cd;
909 border-radius: 5px;
910 font-size: 0.9em;
911 }}
912 </style>
913</head>
914<body>
915 <div class="header">
916 <h1>💬 Q&A Session Report</h1>
917 <p>Document Question & Answer Analysis</p>
918 <p><strong>Session Date:</strong> {session.get('session_date', 'Unknown')}</p>
919 </div>
920
921 <div class="summary">
922 <div class="summary-card">
923 <h3>Total Questions</h3>
924 <div style="font-size: 2em; font-weight: bold; color: #6f42c1;">
925 {session.get('total_questions', 0)}
926 </div>
927 </div>
928 <div class="summary-card">
929 <h3>Successful Answers</h3>
930 <div style="font-size: 2em; font-weight: bold; color: #28a745;">
931 {summary.get('successful_answers', 0)}
932 </div>
933 </div>
934 <div class="summary-card">
935 <h3>Avg Response Length</h3>
936 <div style="font-size: 2em; font-weight: bold; color: #17a2b8;">
937 {summary.get('average_response_length', 0):.0f}
938 </div>
939 </div>
940 </div>
941
942 <h2>📝 Q&A Interactions</h2>
943 """
944
945 # Add individual Q&A items
946 for i, interaction in enumerate(interactions, 1):
947 question = interaction.get('query', 'No question')
948 answer = interaction.get('result', interaction.get('answer', 'No answer'))
949 sources = interaction.get('sources', [])
950
951 html_content += f"""
952 <div class="qa-item">
953 <h3>Question {i}</h3>
954 <div class="question">
955 <strong>Q:</strong> {question}
956 </div>
957 <div class="answer">
958 <strong>A:</strong> {answer}
959 </div>
960 """
961
962 if sources:
963 html_content += '<div class="sources"><strong>Sources:</strong><ul>'
964 for source in sources[:3]: # Limit to first 3 sources
965 content_preview = source.get('content', '')[:200] + '...' if len(source.get('content', '')) > 200 else source.get('content', '')
966 html_content += f'<li>{content_preview}</li>'
967 html_content += '</ul></div>'
968
969 html_content += '</div>'
970
971 html_content += f"""
972
973 <div style="text-align: center; color: #666; margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd;">
974 <p>Generated by GEO SEO AI Optimizer | {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
975 </div>
976</body>
977</html>
978 """
979
980 return html_content
981
982 except Exception as e:
983 return f"<html><body><h1>Q&A HTML Export Error</h1><p>{str(e)}</p></body></html>"
984
985 def _export_qa_csv(self, data: Dict[str, Any]) -> str:
986 """Export Q&A results as CSV"""
987 try:
988 output = io.StringIO()
989 writer = csv.writer(output)
990
991 session = data.get('qa_session', {})
992 summary = data.get('summary', {})
993 interactions = session.get('interactions', [])
994
995 # Header
996 writer.writerow(['Q&A Session Report'])
997 writer.writerow(['Session Date:', session.get('session_date', 'Unknown')])
998 writer.writerow(['Total Questions:', session.get('total_questions', 0)])
999 writer.writerow(['Successful Answers:', summary.get('successful_answers', 0)])
1000 writer.writerow([])
1001
1002 # Q&A data
1003 writer.writerow(['Question Index', 'Question', 'Answer', 'Has Sources', 'Answer Length'])
1004
1005 for i, interaction in enumerate(interactions, 1):
1006 question = interaction.get('query', 'No question')
1007 answer = interaction.get('result', interaction.get('answer', 'No answer'))
1008 has_sources = 'Yes' if interaction.get('sources') else 'No'
1009 answer_length = len(answer) if answer else 0
1010
1011 writer.writerow([i, question, answer, has_sources, answer_length])
1012
1013 return output.getvalue()
1014
1015 except Exception as e:
1016 return f"Q&A CSV export error: {str(e)}"
1017
1018 def _export_batch_excel(self, data: Dict[str, Any]) -> bytes:
1019 """Export batch results as Excel file"""
1020 try:
1021 output = io.BytesIO()
1022
1023 with pd.ExcelWriter(output, engine='openpyxl') as writer:
1024 # Batch metadata sheet
1025 metadata = data.get('batch_metadata', {})
1026 metadata_df = pd.DataFrame([
1027 {'Property': k, 'Value': v} for k, v in metadata.items()
1028 ])
1029 metadata_df.to_excel(writer, sheet_name='Batch Metadata', index=False)
1030
1031 # Batch summary sheet
1032 summary = data.get('batch_summary', {})
1033 summary_df = pd.DataFrame([
1034 {'Metric': k, 'Value': v} for k, v in summary.items()
1035 ])
1036 summary_df.to_excel(writer, sheet_name='Batch Summary', index=False)
1037
1038 # Individual results sheet
1039 results = data.get('batch_results', [])
1040 if results:
1041 # Flatten results for tabular format
1042 flattened_results = []
1043 for i, result in enumerate(results):
1044 flat_result = {'Batch_Index': i}
1045 self._flatten_dict(result, flat_result)
1046 flattened_results.append(flat_result)
1047
1048 results_df = pd.DataFrame(flattened_results)
1049 results_df.to_excel(writer, sheet_name='Batch Results', index=False)
1050
1051 output.seek(0)
1052 return output.getvalue()
1053
1054 except Exception as e:
1055 error_content = f"Batch Excel export failed: {str(e)}\n\nData:\n{json.dumps(data, indent=2)}"
1056 return error_content.encode('utf-8')
1057
1058 def _export_batch_csv(self, data: Dict[str, Any]) -> str:
1059 """Export batch results as CSV"""
1060 try:
1061 output = io.StringIO()
1062 writer = csv.writer(output)
1063
1064 # Batch metadata
1065 metadata = data.get('batch_metadata', {})
1066 writer.writerow(['Batch Analysis Results'])
1067 writer.writerow(['Export Timestamp:', data.get('export_timestamp', 'Unknown')])
1068 writer.writerow([])
1069
1070 writer.writerow(['Batch Metadata'])
1071 for key, value in metadata.items():
1072 writer.writerow([key, value])
1073
1074 writer.writerow([])
1075
1076 # Batch summary
1077 summary = data.get('batch_summary', {})
1078 writer.writerow(['Batch Summary'])
1079 for key, value in summary.items():
1080 writer.writerow([key, value])
1081
1082 writer.writerow([])
1083
1084 # Individual results (simplified)
1085 results = data.get('batch_results', [])
1086 if results:
1087 writer.writerow(['Individual Results'])
1088 writer.writerow(['Index', 'Status', 'Summary'])
1089
1090 for i, result in enumerate(results):
1091 status = 'Success' if not result.get('error') else 'Error'
1092 summary_text = str(result)[:100] + '...' if len(str(result)) > 100 else str(result)
1093 writer.writerow([i, status, summary_text])
1094
1095 return output.getvalue()
1096
1097 except Exception as e:
1098 return f"Batch CSV export error: {str(e)}"
1099
1100 def _export_geo_pdf(self, data: Dict[str, Any]) -> bytes:
1101 """Export GEO data as PDF (placeholder - would need reportlab)"""
1102 try:
1103 # For now, return HTML content as bytes
1104 # In a full implementation, you'd use reportlab or weasyprint
1105 html_content = self._export_geo_html(data)
1106 return html_content.encode('utf-8')
1107
1108 except Exception as e:
1109 error_content = f"PDF export not fully implemented. Error: {str(e)}"
1110 return error_content.encode('utf-8')
1111
1112 def _create_executive_summary(self, analysis_data: Dict[str, Any]) -> Dict[str, Any]:
1113 """Create executive summary report"""
1114 try:
1115 geo_results = analysis_data.get('geo_results', [])
1116 enhancement_results = analysis_data.get('enhancement_results', {})
1117 qa_results = analysis_data.get('qa_results', [])
1118
1119 # Calculate key metrics
1120 overall_performance = self._calculate_overall_performance(analysis_data)
1121
1122 return {
1123 'executive_summary': {
1124 'overall_performance_score': overall_performance,
1125 'key_findings': self._extract_key_findings(analysis_data),
1126 'priority_recommendations': self._get_priority_recommendations(analysis_data),
1127 'roi_potential': self._estimate_roi_potential(overall_performance),
1128 'implementation_timeline': self._suggest_implementation_timeline(analysis_data),
1129 'resource_requirements': self._estimate_resource_requirements(analysis_data)
1130 }
1131 }
1132
1133 except Exception as e:
1134 return {'error': f"Executive summary creation failed: {str(e)}"}
1135
1136 def _create_summary_report(self, analysis_data: Dict[str, Any]) -> Dict[str, Any]:
1137 """Create summary report"""
1138 try:
1139 return {
1140 'summary_report': {
1141 'analysis_overview': self._create_analysis_overview(analysis_data),
1142 'performance_metrics': self._summarize_performance_metrics(analysis_data),
1143 'improvement_opportunities': self._identify_improvement_opportunities(analysis_data),
1144 'competitive_position': self._assess_competitive_position(analysis_data),
1145 'next_steps': self._recommend_next_steps(analysis_data)
1146 }
1147 }
1148
1149 except Exception as e:
1150 return {'error': f"Summary report creation failed: {str(e)}"}
1151
1152 def _create_full_report(self, analysis_data: Dict[str, Any]) -> Dict[str, Any]:
1153 """Create full detailed report"""
1154 try:
1155 return {
1156 'full_report': {
1157 'executive_summary': self._create_executive_summary(analysis_data).get('executive_summary', {}),
1158 'detailed_analysis': {
1159 'geo_analysis_details': analysis_data.get('geo_results', []),
1160 'content_optimization_details': analysis_data.get('enhancement_results', {}),
1161 'qa_performance_details': analysis_data.get('qa_results', [])
1162 },
1163 'methodology': self._document_methodology(),
1164 'data_sources': self._document_data_sources(analysis_data),
1165 'limitations': self._document_limitations(),
1166 'appendices': self._create_appendices(analysis_data)
1167 }
1168 }
1169
1170 except Exception as e:
1171 return {'error': f"Full report creation failed: {str(e)}"}
1172
1173 def _create_batch_summary(self, batch_results: List[Dict[str, Any]]) -> Dict[str, Any]:
1174 """Create summary of batch processing results"""
1175 try:
1176 total_items = len(batch_results)
1177 successful_items = len([r for r in batch_results if not r.get('error')])
1178 failed_items = total_items - successful_items
1179
1180 return {
1181 'total_items': total_items,
1182 'successful_items': successful_items,
1183 'failed_items': failed_items,
1184 'success_rate': (successful_items / total_items * 100) if total_items > 0 else 0,
1185 'processing_status': 'Completed',
1186 'average_processing_time': self._calculate_avg_processing_time(batch_results),
1187 'common_errors': self._identify_common_errors(batch_results)
1188 }
1189
1190 except Exception as e:
1191 return {'error': f"Batch summary creation failed: {str(e)}"}
1192
1193 def _generate_performance_insights(self, scores: Dict[str, float], overall_avg: float) -> List[str]:
1194 """Generate performance insights from scores"""
1195 insights = []
1196
1197 try:
1198 # Overall performance insight
1199 if overall_avg >= 8.0:
1200 insights.append("Excellent overall GEO performance - content is well-optimized for AI search engines")
