""" Verification result formatting components. Similar to MultiClaimVerificationResult.tsx in the React frontend. """ import html from typing import Dict, Any, List from utils.markdown_utils import convert_markdown_to_html def format_verification_results(data: Dict[str, Any]) -> str: """Format verification results in HTML to match the React frontend structure.""" verification_data = data.get("data", {}) verified_claims = verification_data.get("verified_claims", []) if not verified_claims: return """

No Claims Found

No specific claims found to verify in the provided text.

""" # Get summary data summary = verification_data.get("verification_summary", {}) html_parts = [] # Header Section html_parts.append(_format_header(summary)) # Summary Stats Grid html_parts.append(_format_stats_grid(summary)) # Truthfulness Distribution html_parts.append(_format_truthfulness_distribution(summary)) # Individual Claims Section html_parts.append(_format_claims_header()) # Individual Claim Cards for i, claim in enumerate(verified_claims, 1): html_parts.append(_format_claim_card(claim, i)) # Processing Time Breakdown html_parts.append(_format_processing_time(summary)) return "".join(html_parts) def _format_header(summary: Dict[str, Any]) -> str: """Format the header section.""" return f"""

Verification Results

Analyzed {summary.get('total_claims_found', 'N/A')} claims in {summary.get('processing_time', {}).get('total_seconds', 0):.1f} seconds

""" def _format_stats_grid(summary: Dict[str, Any]) -> str: """Format the statistics grid.""" avg_confidence = summary.get('average_confidence', 0) * 100 verification_rate = summary.get('verification_rate', '0%') if isinstance(verification_rate, (int, float)): verification_rate = f"{verification_rate:.1f}%" return f"""
{summary.get('total_claims_found', 'N/A')}
Total Claims
{summary.get('successful_verifications', 'N/A')}
Verified
{avg_confidence:.0f}%
Avg Confidence
{verification_rate}
Success Rate
""" def _format_truthfulness_distribution(summary: Dict[str, Any]) -> str: """Format the truthfulness distribution section.""" truthfulness_dist = summary.get('truthfulness_distribution', {}) if not truthfulness_dist: return "" dist_items = [] color_map = { 'TRUE': '#e8f5e9', 'MOSTLY TRUE': '#e3f2fd', 'NEUTRAL': '#fff8e1', 'MOSTLY FALSE': '#ffebee', 'FALSE': '#ffebee' } for category, count in truthfulness_dist.items(): bg_color = color_map.get(category.upper(), '#f5f5f5') dist_items.append(f"""
{category}
{count}
""") return f"""

Truthfulness Distribution

{"".join(dist_items)}
""" def _format_claims_header() -> str: """Format the claims section header.""" return """

Verified Claims

""" def _format_claim_card(claim: Dict[str, Any], index: int) -> str: """Format an individual claim card.""" truthfulness = claim.get("truthfulness", "UNKNOWN").upper() confidence = claim.get("confidence", 0) claim_text = claim.get("claim", "").strip() evidence = claim.get("evidence", "").strip() explanation = claim.get("explanation", "").strip() sources = claim.get("sources", []) # Status color status_colors = { 'TRUE': 'background: #e8f5e9; color: #2e7d32; border: 1px solid #c8e6c9;', 'MOSTLY TRUE': 'background: #e3f2fd; color: #1565c0; border: 1px solid #bbdefb;', 'NEUTRAL': 'background: #fff8e1; color: #ff8f00; border: 1px solid #ffecb3;', 'MOSTLY FALSE': 'background: #ffebee; color: #c62828; border: 1px solid #ffcdd2;', 'FALSE': 'background: #ffebee; color: #c62828; border: 1px solid #ffcdd2;' } status_style = status_colors.get(truthfulness, 'background: #f5f5f5; color: #424242; border: 1px solid #e0e0e0;') # Confidence color conf_color = '#1565c0' if confidence >= 0.8 else '#ff8f00' if confidence >= 0.6 else '#c62828' # Check if we have valid evidence separate from explanation has_valid_evidence = (evidence and evidence.strip() and evidence.lower() != 'evidence' and evidence != '[object Object]' and '[object Object]' not in evidence and evidence.strip() not in ['# EVIDENCE', 'EVIDENCE', 'Evidence'] and len(evidence.strip()) > 20 and 'Evidence analysis is included in the explanation section' not in evidence) # Format sources sources_html = _format_sources(sources) # Convert markdown to HTML for explanation and evidence explanation_html = convert_markdown_to_html(str(explanation)) if explanation else "No analysis available for this claim." evidence_html = convert_markdown_to_html(str(evidence)) if has_valid_evidence else "" # Only first claim is expanded by default is_expanded = (index == 1) return f"""
{index}

Claim {index}

{truthfulness}
{confidence * 100:.0f}% confidence

{claim_text}

{'Hide Evidence' if is_expanded else 'Show Evidence'}
{f'''

Analysis

{explanation_html}
''' if explanation else ''}

Evidence

{f'
{evidence_html}
' if has_valid_evidence else '''

Detailed evidence analysis is included in the explanation above.

This claim's verification is based on the comprehensive analysis provided.

'''}
{sources_html}
""" def _format_sources(sources: List) -> str: """Format the sources section.""" if not sources: return "" source_links = [] for j, source in enumerate(sources[:3], 1): if source: # Handle both string URLs and source objects if isinstance(source, str): safe_url = html.escape(source) # Extract domain name for title try: domain = source.split('/')[2] if '/' in source and len(source.split('/')) > 2 else source[:30] safe_title = html.escape(domain) except: safe_title = html.escape(source[:30]) else: safe_url = html.escape(source.get('url', '')) safe_title = html.escape(source.get('title', 'Source')) source_links.append(f""" {safe_title} """) return f"""

Sources ({len(sources)})

{"".join(source_links)}
""" def _format_processing_time(summary: Dict[str, Any]) -> str: """Format the processing time breakdown section.""" step_breakdown = summary.get('processing_time', {}).get('step_breakdown', {}) if not step_breakdown: return "" breakdown_items = [] for step, time in step_breakdown.items(): step_name = step.replace('_', ' ').title() breakdown_items.append(f"""
{time:.1f}s
{step_name}
""") return f"""

Processing Time Breakdown

{"".join(breakdown_items)}
"""