#!/usr/bin/env python3 """ Suno Content Evaluation Report Visualizer Interactive web app for viewing and analyzing content evaluation results. """ import os import json import pandas as pd import plotly import plotly.express as px import plotly.graph_objects as go from plotly.subplots import make_subplots from flask import Flask, render_template, request, jsonify, redirect, url_for from werkzeug.utils import secure_filename from datetime import datetime import glob app = Flask(__name__) app.config['UPLOAD_FOLDER'] = 'data' app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max file size # Ensure upload directory exists os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) def parse_report_data(report_data): """Parse JSON report data into structured format""" if 'evaluation_report' in report_data: # Standard report format summary = report_data['evaluation_report']['summary'] results = report_data['evaluation_report']['results'] timestamp = report_data['evaluation_report']['timestamp'] elif 'detailed_evaluation_report' in report_data: # Detailed report format summary = report_data['detailed_evaluation_report']['summary'] results = report_data['detailed_evaluation_report']['results'] timestamp = report_data['detailed_evaluation_report']['timestamp'] else: raise ValueError("Unknown report format") return { 'summary': summary, 'results': results, 'timestamp': timestamp, 'total_images': len(results) } def create_summary_charts(data): """Create summary visualization charts""" summary = data['summary'] # Approval rate pie chart fig_pie = go.Figure(data=[go.Pie( labels=['Approved', 'Rejected'], values=[summary['approved'], summary['rejected']], hole=0.3, marker_colors=['#28a745', '#dc3545'] )]) fig_pie.update_layout( title="Content Approval Overview", showlegend=True, height=400 ) # Rejection reasons bar chart rejection_categories = summary.get('rejection_categories', {}) if rejection_categories: fig_bar = px.bar( x=list(rejection_categories.keys()), y=list(rejection_categories.values()), title="Rejection Reasons", color=list(rejection_categories.values()), color_continuous_scale='Reds' ) fig_bar.update_layout(height=400, showlegend=False) fig_bar.update_xaxis(title="Rejection Category") fig_bar.update_yaxis(title="Count") else: fig_bar = go.Figure() fig_bar.add_annotation( text="No rejections to display", xref="paper", yref="paper", x=0.5, y=0.5, showarrow=False ) fig_bar.update_layout(title="Rejection Reasons", height=400) return { 'pie_chart': json.dumps(fig_pie, cls=plotly.utils.PlotlyJSONEncoder), 'bar_chart': json.dumps(fig_bar, cls=plotly.utils.PlotlyJSONEncoder) } def create_confidence_analysis(results): """Create confidence score analysis""" # Extract confidence scores approved_scores = [r.get('confidence', 0) for r in results if r.get('approved', False)] rejected_scores = [r.get('confidence', 0) for r in results if not r.get('approved', False)] fig = go.Figure() if approved_scores: fig.add_trace(go.Histogram( x=approved_scores, name='Approved', marker_color='green', opacity=0.7, nbinsx=10 )) if rejected_scores: fig.add_trace(go.Histogram( x=rejected_scores, name='Rejected', marker_color='red', opacity=0.7, nbinsx=10 )) fig.update_layout( title="Confidence Score Distribution", xaxis_title="Confidence Score", yaxis_title="Count", barmode='overlay', height=400 ) return json.dumps(fig, cls=plotly.utils.PlotlyJSONEncoder) def create_timeline_chart(results): """Create timeline visualization if timestamps are available""" # Extract file processing order (simplified timeline) approved_count = 0 rejected_count = 0 timeline_data = [] for i, result in enumerate(results): if result.get('approved', False): approved_count += 1 else: rejected_count += 1 timeline_data.append({ 'index': i + 1, 'approved_cumulative': approved_count, 'rejected_cumulative': rejected_count, 'filename': os.path.basename(result.get('link', f'Image {i+1}')) }) df = pd.DataFrame(timeline_data) fig = go.Figure() fig.add_trace(go.Scatter( x=df['index'], y=df['approved_cumulative'], mode='lines+markers', name='Approved (Cumulative)', line=dict(color='green'), hovertemplate='%{customdata}
Approved: %{y}', customdata=df['filename'] )) fig.add_trace(go.Scatter( x=df['index'], y=df['rejected_cumulative'], mode='lines+markers', name='Rejected (Cumulative)', line=dict(color='red'), hovertemplate='%{customdata}
Rejected: %{y}', customdata=df['filename'] )) fig.update_layout( title="Content Evaluation Timeline", xaxis_title="Processing Order", yaxis_title="Cumulative Count", height=400 ) return json.dumps(fig, cls=plotly.utils.PlotlyJSONEncoder) @app.route('/') def index(): """Main dashboard page""" # Look for existing report files report_files = [] for pattern in ['*.json', 'data/*.json', '../*.json', '../reviews/*.json']: report_files.extend(glob.glob(pattern)) # Filter for likely report files report_files = [f for f in report_files if 'report' in f.lower() or 'evaluation' in f.lower()] report_files = sorted(report_files, key=os.path.getmtime, reverse=True) return render_template('index.html', available_reports=report_files) @app.route('/upload', methods=['POST']) def upload_report(): """Handle report file upload""" if 'file' not in request.files: return redirect(url_for('index')) file = request.files['file'] if file.filename == '': return redirect(url_for('index')) if file and file.filename.endswith('.json'): filename = secure_filename(file.filename) filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename) file.save(filepath) return redirect(url_for('view_report', filename=filename)) return redirect(url_for('index')) @app.route('/view/') def view_report(filename): """View a specific report""" # Try different possible locations possible_paths = [ filename, os.path.join('data', filename), os.path.join('..', filename), os.path.join('..', 'reviews', filename) ] filepath = None for path in possible_paths: if os.path.exists(path): filepath = path break if not filepath: return f"Report file not found: {filename}", 404 try: with open(filepath, 'r') as f: report_data = json.load(f) data = parse_report_data(report_data) charts = create_summary_charts(data) confidence_chart = create_confidence_analysis(data['results']) timeline_chart = create_timeline_chart(data['results']) return render_template('dashboard.html', data=data, charts=charts, confidence_chart=confidence_chart, timeline_chart=timeline_chart, filename=filename) except Exception as e: return f"Error loading report: {str(e)}", 500 @app.route('/api/report/') def api_report(filename): """API endpoint for report data""" filepath = os.path.join('data', filename) if not os.path.exists(filepath): return jsonify({'error': 'File not found'}), 404 try: with open(filepath, 'r') as f: report_data = json.load(f) data = parse_report_data(report_data) return jsonify(data) except Exception as e: return jsonify({'error': str(e)}), 500 if __name__ == '__main__': print("🚀 Starting Suno Content Evaluation Report Visualizer") print("📊 Access the dashboard at: http://localhost:5000") app.run(debug=True, host='0.0.0.0', port=5000)