#!/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)