Download utils/visual_output.py from JatinAutonomousLabs/PDF_analyst: direct link, hf CLI and curl.
- Browser
- Download file 12.8 kB
-
https://huggingface.co/spaces/JatinAutonomousLabs/PDF_analyst/resolve/main/utils/visual_output.py
- Command line
-
hf download hf://spaces/JatinAutonomousLabs/PDF_analyst/utils/visual_output.py
-
curl -L -o visual_output.py https://huggingface.co/spaces/JatinAutonomousLabs/PDF_analyst/resolve/main/utils/visual_output.py
12.8 kB
| # 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 | |