From f83c2497cce0a5e96cd5e183ae9e9d84fb3b7196 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 13 Jul 2025 22:56:27 +0530 Subject: [PATCH] Fix data analyzer TypeError and enhance markdown parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Issues Fixed - Fixed TypeError: text.replace is not a function in parseMarkdown - Enhanced type checking for null/undefined inputs in agent-utils.js - Data analyzer now properly handles all response data types ## Enhancements - Improved parseMarkdown function with better type safety - Enhanced markdown parsing with proper HTML styling - Added custom parseMarkdownToHTML for data analyzer with rich formatting - Better handling of headers, lists, bold text, and paragraphs ## Data Analyzer Improvements - Custom markdown parser with styled headers and lists - Proper content extraction priority: report_text > insights_summary > raw_response.analysis - Enhanced visual formatting with CSS variables for consistent theming - Better error handling and fallback content ## Technical Changes - Added typeof checks to prevent non-string inputs causing errors - Enhanced bullet point and numbered list conversion - Improved paragraph formatting and line break handling - Consistent styling across all markdown elements ## Test Results - Data analyzer successfully processes PDF files - Returns proper JSON responses with formatted analysis - Markdown parsing works correctly with complex content structure - No more JavaScript type errors in console 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- data_analyzer/processor.py | 2 +- .../templates/data_analyzer/detail.html | 108 ++++++++++++++++-- static/js/agent-utils.js | 75 ++++++++++-- 3 files changed, 167 insertions(+), 18 deletions(-) diff --git a/data_analyzer/processor.py b/data_analyzer/processor.py index f352a8a..655d24c 100644 --- a/data_analyzer/processor.py +++ b/data_analyzer/processor.py @@ -99,7 +99,7 @@ class DataAnalysisAgentProcessor(StandardWebhookProcessor): } # Use analysis text for multiple fields for compatibility - insights_summary = analysis_text[:500] + '...' if len(analysis_text) > 500 else analysis_text + insights_summary = analysis_text report_text = analysis_text raw_response = response_data diff --git a/data_analyzer/templates/data_analyzer/detail.html b/data_analyzer/templates/data_analyzer/detail.html index 1625558..6bab689 100644 --- a/data_analyzer/templates/data_analyzer/detail.html +++ b/data_analyzer/templates/data_analyzer/detail.html @@ -676,16 +676,108 @@ }); }); - // Display analysis results with markdown parsing + // Display analysis results with custom Data Analyzer formatting function displayResults(result) { - AgentUtils.displayResults({ - result: result, - resultsId: 'analysisResults', - contentId: 'analysisContent', - defaultMessage: 'Data analysis completed successfully!', - successMessage: '✅ Data analysis completed and payment processed!', - errorMessage: '❌ Failed to analyze data - no charge applied' + const resultsContainer = document.getElementById('analysisResults'); + const contentElement = document.getElementById('analysisContent'); + + if (!resultsContainer || !contentElement) { + console.error('Results elements not found'); + return; + } + + // Update wallet balance if provided + if (result.wallet_balance !== undefined) { + updateWalletBalance(result.wallet_balance); + } + + // Handle errors + if (result.error) { + AgentUtils.showToast(`❌ ${result.error}`, 'error'); + return; + } + + // Extract content with priority: report_text > insights_summary > raw_response.analysis + let content = ''; + + if (result.report_text && typeof result.report_text === 'string') { + content = result.report_text; + } else if (result.insights_summary && typeof result.insights_summary === 'string') { + content = result.insights_summary; + } else if (result.raw_response && result.raw_response.analysis && typeof result.raw_response.analysis === 'string') { + content = result.raw_response.analysis; + } else { + content = 'Data analysis completed successfully!'; + } + + // Parse markdown content to HTML + const formattedContent = parseMarkdownToHTML(content); + + // Update content + contentElement.innerHTML = formattedContent; + + // Show results + resultsContainer.style.display = 'block'; + resultsContainer.scrollIntoView({ behavior: 'smooth' }); + + // Show success message + const successMessage = result.success ? '✅ Data analysis completed and payment processed!' : '✅ Data analysis completed!'; + AgentUtils.showToast(successMessage, 'success'); + } + + // Enhanced markdown parser for Data Analyzer results + function parseMarkdownToHTML(markdown) { + if (!markdown || typeof markdown !== 'string') { + return '

No content available.

'; + } + + let html = markdown; + + // Convert headers (### Header, ## Header, # Header) + html = html.replace(/^### (.*$)/gm, '

$1

'); + html = html.replace(/^## (.*$)/gm, '

$1

'); + html = html.replace(/^# (.*$)/gm, '

$1

'); + + // Convert bold text (**text** or __text__) + html = html.replace(/\*\*(.*?)\*\*/g, '$1'); + html = html.replace(/__(.*?)__/g, '$1'); + + // Convert italic text (*text* or _text_) + html = html.replace(/\*(.*?)\*/g, '$1'); + html = html.replace(/_(.*?)_/g, '$1'); + + // Convert bullet points (- item or * item) + html = html.replace(/^[\s]*[-\*]\s+(.*)$/gm, '
  • $1
  • '); + + // Wrap consecutive list items in ul tags + html = html.replace(/(]*>.*?<\/li>\s*)+/gs, function(match) { + return `
      ${match}
    `; }); + + // Convert numbered lists (1. item, 2. item) + html = html.replace(/^\s*\d+\.\s+(.*)$/gm, '
  • $1
  • '); + + // Wrap consecutive numbered list items in ol tags + html = html.replace(/(]*>.*?<\/li>\s*)+/gs, function(match) { + if (match.includes('ul style')) return match; // Skip if already wrapped in ul + return `
      ${match}
    `; + }); + + // Convert line breaks to paragraphs + const paragraphs = html.split(/\n\s*\n/); + html = paragraphs.map(p => { + const trimmed = p.trim(); + if (trimmed === '') return ''; + + // Skip if already wrapped in HTML tags + if (trimmed.startsWith('${trimmed}

    `; + }).filter(p => p !== '').join(''); + + return html; } // Track polling and results to prevent duplicates diff --git a/static/js/agent-utils.js b/static/js/agent-utils.js index 1814dd3..f532c96 100644 --- a/static/js/agent-utils.js +++ b/static/js/agent-utils.js @@ -5,18 +5,75 @@ window.AgentUtils = { /** - * Simple Text Formatter - * Cleans AI-generated text and converts to readable HTML + * Enhanced Markdown Parser + * Converts markdown syntax to styled HTML with proper formatting */ parseMarkdown(text) { - if (!text) return ''; + // Handle null, undefined, or non-string inputs + if (!text || typeof text !== 'string') { + return ''; + } - return text - .replace(/\*\*/g, '') // Remove markdown bold syntax - .replace(/\#{1,3}\s/g, '') // Remove header syntax - .replace(/\n{3,}/g, '\n\n') // Reduce excessive line breaks - .replace(/\n/g, '
    ') // Convert line breaks to HTML - .trim(); + let html = text; + + // Convert headers with proper styling + html = html.replace(/^### (.*$)/gm, '

    $1

    '); + html = html.replace(/^## (.*$)/gm, '

    $1

    '); + html = html.replace(/^# (.*$)/gm, '

    $1

    '); + + // Convert bold text + html = html.replace(/\*\*(.*?)\*\*/g, '$1'); + + // Convert bullet points to proper lists + const lines = html.split('\n'); + let inList = false; + let processedLines = []; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i].trim(); + + if (line.startsWith('- ')) { + if (!inList) { + processedLines.push('
      '); + inList = true; + } + processedLines.push(`
    • ${line.substring(2).trim()}
    • `); + } else { + if (inList) { + processedLines.push('
    '); + inList = false; + } + processedLines.push(line); + } + } + + if (inList) { + processedLines.push(''); + } + + html = processedLines.join('\n'); + + // Reduce excessive line breaks and convert to paragraphs + html = html.replace(/\n{3,}/g, '\n\n'); + + // Convert double line breaks to paragraph breaks + const paragraphs = html.split('\n\n'); + html = paragraphs + .filter(p => p.trim() !== '') + .map(p => { + const trimmed = p.trim(); + // Don't wrap headers or lists in paragraphs + if (trimmed.startsWith('') || trimmed.includes('${trimmed}

    `; + }) + .join(''); + + // Convert remaining single line breaks to
    tags + html = html.replace(/\n/g, '
    '); + + return html.trim(); }, /**