# utils/visual_output.py - Visual output generation for PDF Analysis & Orchestrator
import json
import re
from typing import Dict, List, Any, Optional
from datetime import datetime
class VisualOutputGenerator:
"""Generate visual representations of analysis results"""
def __init__(self):
self.visual_elements = []
def create_infographic(self, data: Dict[str, Any], title: str = "Analysis Summary") -> str:
"""Create an infographic-style summary"""
visual = f"""
## đ {title}
"""
# Key metrics
if 'metrics' in data:
visual += f"""
"""
for metric, value in data['metrics'].items():
visual += f"""
"""
visual += "
"
visual += "
"
return visual
def create_data_table(self, data: List[Dict[str, Any]], title: str = "Data Table") -> str:
"""Create a formatted table from data"""
if not data:
return ""
# Get headers from first row
headers = list(data[0].keys())
table = f"""
## đ {title}
| {' | '.join(headers)} |
| {' | '.join(['---'] * len(headers))} |
"""
for row in data:
values = [str(row.get(header, '')) for header in headers]
table += f"| {' | '.join(values)} |\n"
return table
def create_progress_bar(self, value: float, max_value: float, label: str) -> str:
"""Create a progress bar visualization"""
percentage = min(100, (value / max_value) * 100) if max_value > 0 else 0
return f"""
{label}: {value:.1f}/{max_value:.1f} ({percentage:.1f}%)
"""
def create_timeline(self, events: List[Dict[str, str]], title: str = "Timeline") -> str:
"""Create a timeline visualization"""
timeline = f"""
## â° {title}
"""
for i, event in enumerate(events):
timeline += f"""
{event.get('title', 'Event')}
{event.get('description', '')}
{event.get('date', '')}
"""
timeline += "
"
return timeline
def create_comparison_chart(self, data: Dict[str, float], title: str = "Comparison") -> str:
"""Create a comparison chart"""
if not data:
return ""
max_value = max(data.values()) if data.values() else 1
chart = f"""
## đ {title}
"""
for label, value in data.items():
percentage = (value / max_value) * 100
chart += f"""
"""
chart += "
"
return chart
def create_key_points(self, points: List[str], title: str = "Key Points") -> str:
"""Create a stunning key points section with visual elements"""
if not points:
return ""
# Icons for different types of points
icons = ["đ¯", "đĄ", "â
", "đ", "âĄ", "đ", "đ", "đ", "â", "đĨ"]
visual = f"""
## đĄ {title}
"""
for i, point in enumerate(points, 1):
icon = icons[i % len(icons)]
color = ["#007bff", "#28a745", "#ffc107", "#dc3545", "#6f42c1"][i % 5]
visual += f"""
"""
visual += "
"
return visual
def create_alert_box(self, message: str, alert_type: str = "info") -> str:
"""Create an alert box"""
colors = {
"info": "#2196F3",
"success": "#4CAF50",
"warning": "#FF9800",
"error": "#F44336"
}
icons = {
"info": "âšī¸",
"success": "â
",
"warning": "â ī¸",
"error": "â"
}
color = colors.get(alert_type, colors["info"])
icon = icons.get(alert_type, icons["info"])
return f"""
"""
def create_metric_cards(self, metrics: Dict[str, Any], title: str = "Key Metrics") -> str:
"""Create metric cards"""
if not metrics:
return ""
cards = f"""
## đ {title}
"""
for metric, value in metrics.items():
cards += f"""
"""
cards += "
"
return cards
def format_analysis_with_visuals(self, analysis_text: str, document_metadata: Dict[str, Any] = None) -> str:
"""Format analysis text with stunning visual elements"""
visual_elements = []
# Add document info if available
if document_metadata:
visual_elements.append(self.create_metric_cards({
"đ Pages": document_metadata.get('page_count', 'Unknown'),
"đž File Size": f"{document_metadata.get('file_size', 0) / 1024:.1f} KB",
"⥠Processing": f"{document_metadata.get('processing_time', 0):.1f}s",
"đ¯ Tokens": document_metadata.get('tokens_used', 'N/A')
}, "đ Document Overview"))
# Create a beautiful header
visual_elements.append(self.create_analysis_header())
# Try to extract key points from analysis
key_points = self._extract_key_points(analysis_text)
if key_points:
visual_elements.append(self.create_key_points(key_points, "đ¯ Key Insights"))
# Try to extract metrics
metrics = self._extract_metrics(analysis_text)
if metrics:
visual_elements.append(self.create_metric_cards(metrics, "đ Key Metrics"))
# Try to extract data for tables
table_data = self._extract_table_data(analysis_text)
if table_data:
visual_elements.append(self.create_data_table(table_data, "đ Data Summary"))
# Format the main analysis with better structure
formatted_analysis = self._format_analysis_text(analysis_text)
# Combine all elements
result = "\n\n".join(visual_elements)
if formatted_analysis:
result += f"\n\n---\n\n{formatted_analysis}"
return result
def create_analysis_header(self) -> str:
"""Create a beautiful analysis header"""
return """
đ AI Document Analysis
Powered by Advanced AI âĸ Instant Insights âĸ Professional Results
"""
def _format_analysis_text(self, text: str) -> str:
"""Format analysis text with better visual structure"""
# Split into sections
sections = text.split('\n\n')
formatted_sections = []
for section in sections:
if section.strip():
# Check if it's a header
if section.startswith('##'):
formatted_sections.append(f"\n{section}\n")
else:
# Format as a content block
formatted_sections.append(f"""
{section}
""")
return '\n'.join(formatted_sections)
def _extract_table_data(self, text: str) -> List[Dict[str, Any]]:
"""Extract data that can be formatted as tables"""
table_data = []
# Look for comparison patterns
comparison_pattern = r'(\w+):\s*(\d+(?:\.\d+)?%?)\s*vs\s*(\w+):\s*(\d+(?:\.\d+)?%?)'
matches = re.findall(comparison_pattern, text, re.IGNORECASE)
for match in matches:
table_data.append({
"Metric": match[0],
"Value": match[1],
"Comparison": match[2],
"Value 2": match[3]
})
return table_data
def _extract_key_points(self, text: str) -> List[str]:
"""Extract key points from analysis text"""
# Look for bullet points, numbered lists, or key findings
points = []
# Extract bullet points
bullet_pattern = r'[-âĸ*]\s+(.+?)(?=\n|$)'
bullets = re.findall(bullet_pattern, text, re.MULTILINE)
points.extend([bullet.strip() for bullet in bullets if len(bullet.strip()) > 10])
# Extract numbered points
number_pattern = r'\d+\.\s+(.+?)(?=\n|$)'
numbers = re.findall(number_pattern, text, re.MULTILINE)
points.extend([num.strip() for num in numbers if len(num.strip()) > 10])
# Limit to top 5 points
return points[:5]
def _extract_metrics(self, text: str) -> Dict[str, str]:
"""Extract metrics from analysis text"""
metrics = {}
# Look for percentage patterns
percent_pattern = r'(\d+(?:\.\d+)?%)'
percentages = re.findall(percent_pattern, text)
if percentages:
metrics["Success Rate"] = percentages[0]
# Look for number patterns
number_pattern = r'(\d+(?:,\d+)*(?:\.\d+)?)\s+(?:pages?|items?|points?|years?|months?)'
numbers = re.findall(number_pattern, text, re.IGNORECASE)
if numbers:
metrics["Total Items"] = numbers[0]
return metrics