| | |
| | 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 |
| |
|