File size: 12,838 Bytes
bb3909a 73f15b1 bb3909a 73f15b1 bb3909a 73f15b1 bb3909a 73f15b1 bb3909a 73f15b1 bb3909a 73f15b1 bb3909a 73f15b1 bb3909a 73f15b1 bb3909a 73f15b1 bb3909a 73f15b1 bb3909a 73f15b1 bb3909a 73f15b1 bb3909a |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 |
# 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}
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 20px; border-radius: 10px; color: white; margin: 10px 0;">
"""
# Key metrics
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 ""
# 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"""
<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 for different types of points
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 = []
# 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 """
<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"""
# 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"""
<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 = []
# 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
|