|
|
|
|
|
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} |
|
|
|
|
|
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 20px; border-radius: 10px; color: white; margin: 10px 0;"> |
|
|
""" |
|
|
|
|
|
|
|
|
if 'metrics' in data: |
|
|
visual += f""" |
|
|
<div style="display: flex; justify-content: space-around; margin: 20px 0;"> |
|
|
""" |
|
|
for metric, value in data['metrics'].items(): |
|
|
visual += f""" |
|
|
<div style="text-align: center; background: rgba(255,255,255,0.2); padding: 15px; border-radius: 8px; margin: 5px;"> |
|
|
<h3 style="margin: 0; font-size: 24px;">{value}</h3> |
|
|
<p style="margin: 5px 0 0 0; font-size: 14px;">{metric}</p> |
|
|
</div> |
|
|
""" |
|
|
visual += "</div>" |
|
|
|
|
|
visual += "</div>" |
|
|
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 "" |
|
|
|
|
|
|
|
|
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""" |
|
|
<div style="margin: 10px 0;"> |
|
|
<p style="margin: 5px 0; font-weight: bold;">{label}: {value:.1f}/{max_value:.1f} ({percentage:.1f}%)</p> |
|
|
<div style="background: #e0e0e0; border-radius: 10px; height: 20px; overflow: hidden;"> |
|
|
<div style="background: linear-gradient(90deg, #4CAF50, #8BC34A); height: 100%; width: {percentage}%; transition: width 0.3s ease;"></div> |
|
|
</div> |
|
|
</div> |
|
|
""" |
|
|
|
|
|
def create_timeline(self, events: List[Dict[str, str]], title: str = "Timeline") -> str: |
|
|
"""Create a timeline visualization""" |
|
|
timeline = f""" |
|
|
## β° {title} |
|
|
|
|
|
<div style="position: relative; padding-left: 30px; margin: 20px 0;"> |
|
|
""" |
|
|
|
|
|
for i, event in enumerate(events): |
|
|
timeline += f""" |
|
|
<div style="position: relative; margin-bottom: 20px;"> |
|
|
<div style="position: absolute; left: -25px; top: 5px; width: 12px; height: 12px; background: #4CAF50; border-radius: 50%; border: 3px solid white; box-shadow: 0 0 0 3px #4CAF50;"></div> |
|
|
<div style="background: #f5f5f5; padding: 15px; border-radius: 8px; border-left: 4px solid #4CAF50;"> |
|
|
<h4 style="margin: 0 0 5px 0; color: #333;">{event.get('title', 'Event')}</h4> |
|
|
<p style="margin: 0; color: #666;">{event.get('description', '')}</p> |
|
|
<small style="color: #999;">{event.get('date', '')}</small> |
|
|
</div> |
|
|
</div> |
|
|
""" |
|
|
|
|
|
timeline += "</div>" |
|
|
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} |
|
|
|
|
|
<div style="margin: 20px 0;"> |
|
|
""" |
|
|
|
|
|
for label, value in data.items(): |
|
|
percentage = (value / max_value) * 100 |
|
|
chart += f""" |
|
|
<div style="margin: 10px 0;"> |
|
|
<div style="display: flex; justify-content: space-between; margin-bottom: 5px;"> |
|
|
<span style="font-weight: bold;">{label}</span> |
|
|
<span style="color: #666;">{value:.1f}</span> |
|
|
</div> |
|
|
<div style="background: #e0e0e0; border-radius: 5px; height: 20px; overflow: hidden;"> |
|
|
<div style="background: linear-gradient(90deg, #2196F3, #21CBF3); height: 100%; width: {percentage}%; transition: width 0.3s ease;"></div> |
|
|
</div> |
|
|
</div> |
|
|
""" |
|
|
|
|
|
chart += "</div>" |
|
|
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 = ["π―", "π‘", "β
", "π", "β‘", "π", "π", "π", "β", "π₯"] |
|
|
|
|
|
visual = f""" |
|
|
## π‘ {title} |
|
|
|
|
|
<div style="display: grid; gap: 20px; margin: 25px 0;"> |
|
|
""" |
|
|
|
|
|
for i, point in enumerate(points, 1): |
|
|
icon = icons[i % len(icons)] |
|
|
color = ["#007bff", "#28a745", "#ffc107", "#dc3545", "#6f42c1"][i % 5] |
|
|
|
|
|
visual += f""" |
|
|
<div style="background: linear-gradient(135deg, {color}15, {color}05); border: 2px solid {color}30; padding: 20px; border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); transition: transform 0.2s ease;"> |
|
|
<div style="display: flex; align-items: flex-start; gap: 15px;"> |
|
|
<div style="background: {color}; color: white; border-radius: 50%; width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; font-size: 16px; font-weight: bold; flex-shrink: 0; box-shadow: 0 2px 8px {color}50;">{icon}</div> |
|
|
<div style="flex: 1;"> |
|
|
<p style="margin: 0; line-height: 1.6; font-size: 16px; color: #333; font-weight: 500;">{point}</p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
""" |
|
|
|
|
|
visual += "</div>" |
|
|
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""" |
|
|
<div style="background: {color}15; border: 1px solid {color}; border-radius: 8px; padding: 15px; margin: 15px 0; display: flex; align-items: flex-start;"> |
|
|
<span style="font-size: 20px; margin-right: 10px;">{icon}</span> |
|
|
<p style="margin: 0; color: {color}; font-weight: 500;">{message}</p> |
|
|
</div> |
|
|
""" |
|
|
|
|
|
def create_metric_cards(self, metrics: Dict[str, Any], title: str = "Key Metrics") -> str: |
|
|
"""Create metric cards""" |
|
|
if not metrics: |
|
|
return "" |
|
|
|
|
|
cards = f""" |
|
|
## π {title} |
|
|
|
|
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin: 20px 0;"> |
|
|
""" |
|
|
|
|
|
for metric, value in metrics.items(): |
|
|
cards += f""" |
|
|
<div style="background: white; border: 1px solid #e0e0e0; border-radius: 8px; padding: 20px; text-align: center; box-shadow: 0 2px 4px rgba(0,0,0,0.1);"> |
|
|
<h3 style="margin: 0 0 10px 0; color: #333; font-size: 28px;">{value}</h3> |
|
|
<p style="margin: 0; color: #666; font-size: 14px; text-transform: uppercase; letter-spacing: 0.5px;">{metric}</p> |
|
|
</div> |
|
|
""" |
|
|
|
|
|
cards += "</div>" |
|
|
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 = [] |
|
|
|
|
|
|
|
|
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")) |
|
|
|
|
|
|
|
|
visual_elements.append(self.create_analysis_header()) |
|
|
|
|
|
|
|
|
key_points = self._extract_key_points(analysis_text) |
|
|
if key_points: |
|
|
visual_elements.append(self.create_key_points(key_points, "π― Key Insights")) |
|
|
|
|
|
|
|
|
metrics = self._extract_metrics(analysis_text) |
|
|
if metrics: |
|
|
visual_elements.append(self.create_metric_cards(metrics, "π Key Metrics")) |
|
|
|
|
|
|
|
|
table_data = self._extract_table_data(analysis_text) |
|
|
if table_data: |
|
|
visual_elements.append(self.create_data_table(table_data, "π Data Summary")) |
|
|
|
|
|
|
|
|
formatted_analysis = self._format_analysis_text(analysis_text) |
|
|
|
|
|
|
|
|
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 """ |
|
|
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 25px; border-radius: 15px; color: white; margin: 20px 0; text-align: center; box-shadow: 0 8px 32px rgba(0,0,0,0.1);"> |
|
|
<h1 style="margin: 0 0 10px 0; font-size: 28px; font-weight: 700;">π AI Document Analysis</h1> |
|
|
<p style="margin: 0; font-size: 16px; opacity: 0.9;">Powered by Advanced AI β’ Instant Insights β’ Professional Results</p> |
|
|
</div> |
|
|
""" |
|
|
|
|
|
def _format_analysis_text(self, text: str) -> str: |
|
|
"""Format analysis text with better visual structure""" |
|
|
|
|
|
sections = text.split('\n\n') |
|
|
formatted_sections = [] |
|
|
|
|
|
for section in sections: |
|
|
if section.strip(): |
|
|
|
|
|
if section.startswith('##'): |
|
|
formatted_sections.append(f"\n{section}\n") |
|
|
else: |
|
|
|
|
|
formatted_sections.append(f""" |
|
|
<div style="background: #f8f9fa; border-left: 4px solid #007bff; padding: 20px; margin: 15px 0; border-radius: 0 8px 8px 0; box-shadow: 0 2px 8px rgba(0,0,0,0.05);"> |
|
|
{section} |
|
|
</div> |
|
|
""") |
|
|
|
|
|
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 = [] |
|
|
|
|
|
|
|
|
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""" |
|
|
|
|
|
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]) |
|
|
|
|
|
|
|
|
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]) |
|
|
|
|
|
|
|
|
return points[:5] |
|
|
|
|
|
def _extract_metrics(self, text: str) -> Dict[str, str]: |
|
|
"""Extract metrics from analysis text""" |
|
|
metrics = {} |
|
|
|
|
|
|
|
|
percent_pattern = r'(\d+(?:\.\d+)?%)' |
|
|
percentages = re.findall(percent_pattern, text) |
|
|
if percentages: |
|
|
metrics["Success Rate"] = percentages[0] |
|
|
|
|
|
|
|
|
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 |
|
|
|