Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
| from flask import Blueprint, render_template, current_app, jsonify, request, redirect, url_for, flash | |
| from models import db, User, Model, Vote, EloHistory, ModelType | |
| from auth import admin_required | |
| from sqlalchemy import func, desc, extract | |
| from datetime import datetime, timedelta | |
| import json | |
| import os | |
| admin = Blueprint("admin", __name__, url_prefix="/admin") | |
| def index(): | |
| """Admin dashboard homepage""" | |
| # Get count statistics | |
| stats = { | |
| "total_users": User.query.count(), | |
| "total_votes": Vote.query.count(), | |
| "tts_votes": Vote.query.filter_by(model_type=ModelType.TTS).count(), | |
| "conversational_votes": Vote.query.filter_by(model_type=ModelType.CONVERSATIONAL).count(), | |
| "tts_models": Model.query.filter_by(model_type=ModelType.TTS).count(), | |
| "conversational_models": Model.query.filter_by(model_type=ModelType.CONVERSATIONAL).count(), | |
| } | |
| # Get recent votes | |
| recent_votes = Vote.query.order_by(Vote.vote_date.desc()).limit(10).all() | |
| # Get recent users | |
| recent_users = User.query.order_by(User.join_date.desc()).limit(10).all() | |
| # Get daily votes for the past 30 days | |
| thirty_days_ago = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0) - timedelta(days=30) | |
| daily_votes = db.session.query( | |
| func.date(Vote.vote_date).label('date'), | |
| func.count().label('count') | |
| ).filter(Vote.vote_date >= thirty_days_ago).group_by( | |
| func.date(Vote.vote_date) | |
| ).order_by(func.date(Vote.vote_date)).all() | |
| # Generate a complete list of dates for the past 30 days | |
| date_list = [] | |
| current_date = datetime.utcnow() | |
| for i in range(30, -1, -1): | |
| date_list.append((current_date - timedelta(days=i)).date()) | |
| # Create a dictionary with actual vote counts | |
| vote_counts = {day.date: day.count for day in daily_votes} | |
| # Build complete datasets including days with zero votes | |
| formatted_dates = [date.strftime("%Y-%m-%d") for date in date_list] | |
| vote_counts_list = [vote_counts.get(date, 0) for date in date_list] | |
| daily_votes_data = { | |
| "labels": formatted_dates, | |
| "counts": vote_counts_list | |
| } | |
| # Get top models | |
| top_tts_models = Model.query.filter_by( | |
| model_type=ModelType.TTS | |
| ).order_by(Model.current_elo.desc()).limit(5).all() | |
| top_conversational_models = Model.query.filter_by( | |
| model_type=ModelType.CONVERSATIONAL | |
| ).order_by(Model.current_elo.desc()).limit(5).all() | |
| return render_template( | |
| "admin/index.html", | |
| stats=stats, | |
| recent_votes=recent_votes, | |
| recent_users=recent_users, | |
| daily_votes_data=json.dumps(daily_votes_data), | |
| top_tts_models=top_tts_models, | |
| top_conversational_models=top_conversational_models | |
| ) | |
| def models(): | |
| """Manage models""" | |
| tts_models = Model.query.filter_by(model_type=ModelType.TTS).order_by(Model.name).all() | |
| conversational_models = Model.query.filter_by(model_type=ModelType.CONVERSATIONAL).order_by(Model.name).all() | |
| return render_template( | |
| "admin/models.html", | |
| tts_models=tts_models, | |
| conversational_models=conversational_models | |
| ) | |
| def edit_model(model_id): | |
| """Edit a model""" | |
| model = Model.query.get_or_404(model_id) | |
| if request.method == "POST": | |
| model.name = request.form.get("name") | |
| model.is_active = "is_active" in request.form | |
| model.is_open = "is_open" in request.form | |
| model.model_url = request.form.get("model_url") | |
| db.session.commit() | |
| flash(f"Model '{model.name}' updated successfully", "success") | |
| return redirect(url_for("admin.models")) | |
| return render_template("admin/edit_model.html", model=model) | |
| def users(): | |
| """Manage users""" | |
| users = User.query.order_by(User.username).all() | |
| admin_users = os.getenv("ADMIN_USERS", "").split(",") | |
| admin_users = [username.strip() for username in admin_users] | |
| return render_template("admin/users.html", users=users, admin_users=admin_users) | |
| def user_detail(user_id): | |
| """View user details""" | |
| user = User.query.get_or_404(user_id) | |
| # Get user votes | |
| recent_votes = Vote.query.filter_by(user_id=user_id).order_by(Vote.vote_date.desc()).limit(20).all() | |
| # Get vote statistics | |
| tts_votes = Vote.query.filter_by(user_id=user_id, model_type=ModelType.TTS).count() | |
| conversational_votes = Vote.query.filter_by(user_id=user_id, model_type=ModelType.CONVERSATIONAL).count() | |
| # Get favorite models (most chosen) | |
| favorite_models = db.session.query( | |
| Vote.model_chosen, | |
| Model.name, | |
| func.count().label('count') | |
| ).join( | |
| Model, Vote.model_chosen == Model.id | |
| ).filter( | |
| Vote.user_id == user_id | |
| ).group_by( | |
| Vote.model_chosen, Model.name | |
| ).order_by( | |
| desc('count') | |
| ).limit(5).all() | |
| return render_template( | |
| "admin/user_detail.html", | |
| user=user, | |
| recent_votes=recent_votes, | |
| tts_votes=tts_votes, | |
| conversational_votes=conversational_votes, | |
| favorite_models=favorite_models, | |
| total_votes=tts_votes + conversational_votes | |
| ) | |
| def votes(): | |
| """View recent votes""" | |
| page = request.args.get('page', 1, type=int) | |
| per_page = 50 | |
| # Get votes with pagination | |
| votes_pagination = Vote.query.order_by( | |
| Vote.vote_date.desc() | |
| ).paginate(page=page, per_page=per_page) | |
| return render_template( | |
| "admin/votes.html", | |
| votes=votes_pagination.items, | |
| pagination=votes_pagination | |
| ) | |
| def statistics(): | |
| """View detailed statistics""" | |
| # Get daily votes for the past 30 days by model type | |
| thirty_days_ago = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0) - timedelta(days=30) | |
| tts_daily_votes = db.session.query( | |
| func.date(Vote.vote_date).label('date'), | |
| func.count().label('count') | |
| ).filter( | |
| Vote.vote_date >= thirty_days_ago, | |
| Vote.model_type == ModelType.TTS | |
| ).group_by( | |
| func.date(Vote.vote_date) | |
| ).order_by(func.date(Vote.vote_date)).all() | |
| conv_daily_votes = db.session.query( | |
| func.date(Vote.vote_date).label('date'), | |
| func.count().label('count') | |
| ).filter( | |
| Vote.vote_date >= thirty_days_ago, | |
| Vote.model_type == ModelType.CONVERSATIONAL | |
| ).group_by( | |
| func.date(Vote.vote_date) | |
| ).order_by(func.date(Vote.vote_date)).all() | |
| # Monthly new users | |
| monthly_users = db.session.query( | |
| extract('year', User.join_date).label('year'), | |
| extract('month', User.join_date).label('month'), | |
| func.count().label('count') | |
| ).group_by( | |
| 'year', 'month' | |
| ).order_by('year', 'month').all() | |
| # Generate a complete list of dates for the past 30 days | |
| date_list = [] | |
| current_date = datetime.utcnow() | |
| for i in range(30, -1, -1): | |
| date_list.append((current_date - timedelta(days=i)).date()) | |
| # Create dictionaries with actual vote counts | |
| tts_vote_counts = {day.date: day.count for day in tts_daily_votes} | |
| conv_vote_counts = {day.date: day.count for day in conv_daily_votes} | |
| # Format dates consistently for charts | |
| formatted_dates = [date.strftime("%Y-%m-%d") for date in date_list] | |
| # Build complete datasets including days with zero votes | |
| tts_counts = [tts_vote_counts.get(date, 0) for date in date_list] | |
| conv_counts = [conv_vote_counts.get(date, 0) for date in date_list] | |
| # Generate all month/year combinations for the past 12 months | |
| current_date = datetime.utcnow() | |
| month_list = [] | |
| for i in range(11, -1, -1): | |
| past_date = current_date - timedelta(days=i*30) # Approximate | |
| month_list.append((past_date.year, past_date.month)) | |
| # Create a dictionary with actual user counts | |
| user_counts = {(record.year, record.month): record.count for record in monthly_users} | |
| # Build complete monthly datasets including months with zero new users | |
| monthly_labels = [f"{month}/{year}" for year, month in month_list] | |
| monthly_counts = [user_counts.get((year, month), 0) for year, month in month_list] | |
| # Model performance over time | |
| top_models = Model.query.order_by(Model.match_count.desc()).limit(5).all() | |
| # Get first and last timestamp to create a consistent timeline | |
| earliest = datetime.utcnow() - timedelta(days=30) # Default to 30 days ago | |
| latest = datetime.utcnow() # Default to now | |
| # Find actual earliest and latest timestamps across all models | |
| has_elo_history = False | |
| for model in top_models: | |
| first = EloHistory.query.filter_by(model_id=model.id).order_by(EloHistory.timestamp).first() | |
| last = EloHistory.query.filter_by(model_id=model.id).order_by(EloHistory.timestamp.desc()).first() | |
| if first and last: | |
| has_elo_history = True | |
| if first.timestamp < earliest: | |
| earliest = first.timestamp | |
| if last.timestamp > latest: | |
| latest = last.timestamp | |
| # If no history was found, use a default range of the last 30 days | |
| if not has_elo_history: | |
| earliest = datetime.utcnow() - timedelta(days=30) | |
| latest = datetime.utcnow() | |
| # Make sure the date range is valid (earliest before latest) | |
| if earliest > latest: | |
| earliest = latest - timedelta(days=30) | |
| # Generate a list of dates for the ELO history timeline | |
| # Using 1-day intervals for a smoother chart | |
| elo_dates = [] | |
| current = earliest | |
| while current <= latest: | |
| elo_dates.append(current.date()) | |
| current += timedelta(days=1) | |
| # Format dates consistently | |
| formatted_elo_dates = [date.strftime("%Y-%m-%d") for date in elo_dates] | |
| model_history = {} | |
| # Initialize empty data for all top models | |
| for model in top_models: | |
| model_history[model.name] = { | |
| "timestamps": formatted_elo_dates, | |
| "scores": [None] * len(formatted_elo_dates) # Initialize with None values | |
| } | |
| history = EloHistory.query.filter_by( | |
| model_id=model.id | |
| ).order_by(EloHistory.timestamp).all() | |
| if history: | |
| # Create a dictionary mapping dates to scores | |
| history_dict = {} | |
| for h in history: | |
| date_key = h.timestamp.date().strftime("%Y-%m-%d") | |
| history_dict[date_key] = h.elo_score | |
| # Fill in missing dates with the previous score | |
| last_score = model.current_elo # Default to current ELO if no history | |
| scores = [] | |
| for date in formatted_elo_dates: | |
| if date in history_dict: | |
| last_score = history_dict[date] | |
| scores.append(last_score) | |
| model_history[model.name]["scores"] = scores | |
| else: | |
| # If no history, use the current Elo for all dates | |
| model_history[model.name]["scores"] = [model.current_elo] * len(formatted_elo_dates) | |
| chart_data = { | |
| "dailyVotes": { | |
| "labels": formatted_dates, | |
| "ttsCounts": tts_counts, | |
| "convCounts": conv_counts | |
| }, | |
| "monthlyUsers": { | |
| "labels": monthly_labels, | |
| "counts": monthly_counts | |
| }, | |
| "modelHistory": model_history | |
| } | |
| return render_template( | |
| "admin/statistics.html", | |
| chart_data=json.dumps(chart_data) | |
| ) | |
| def activity(): | |
| """View recent text generations""" | |
| # Check if we have any active sessions from app.py | |
| tts_session_count = 0 | |
| conversational_session_count = 0 | |
| # Access global variables from app.py through current_app | |
| if hasattr(current_app, 'tts_sessions'): | |
| tts_session_count = len(current_app.tts_sessions) | |
| else: # Try to access through app module | |
| from app import tts_sessions | |
| tts_session_count = len(tts_sessions) | |
| if hasattr(current_app, 'conversational_sessions'): | |
| conversational_session_count = len(current_app.conversational_sessions) | |
| else: # Try to access through app module | |
| from app import conversational_sessions | |
| conversational_session_count = len(conversational_sessions) | |
| # Get recent votes which represent completed generations | |
| recent_tts_votes = Vote.query.filter_by( | |
| model_type=ModelType.TTS | |
| ).order_by(Vote.vote_date.desc()).limit(20).all() | |
| recent_conv_votes = Vote.query.filter_by( | |
| model_type=ModelType.CONVERSATIONAL | |
| ).order_by(Vote.vote_date.desc()).limit(20).all() | |
| # Get votes per hour for the last 24 hours | |
| current_time = datetime.utcnow() | |
| last_24h = current_time.replace(minute=0, second=0, microsecond=0) - timedelta(hours=24) | |
| # Use SQLite-compatible date formatting | |
| hourly_votes = db.session.query( | |
| func.strftime('%Y-%m-%d %H:00', Vote.vote_date).label('hour'), | |
| func.count().label('count') | |
| ).filter( | |
| Vote.vote_date >= last_24h | |
| ).group_by('hour').order_by('hour').all() | |
| # Generate all hours for the past 24 hours with correct hour formatting | |
| hour_list = [] | |
| for i in range(24, -1, -1): | |
| # Calculate the hour time and truncate to hour | |
| hour_time = current_time - timedelta(hours=i) | |
| hour_time = hour_time.replace(minute=0, second=0, microsecond=0) | |
| hour_list.append(hour_time.strftime('%Y-%m-%d %H:00')) | |
| # Create a dictionary with actual vote counts | |
| vote_counts = {hour.hour: hour.count for hour in hourly_votes} | |
| # Build complete hourly datasets including hours with zero votes | |
| hourly_data = { | |
| "labels": hour_list, | |
| "counts": [vote_counts.get(hour, 0) for hour in hour_list] | |
| } | |
| return render_template( | |
| "admin/activity.html", | |
| tts_session_count=tts_session_count, | |
| conversational_session_count=conversational_session_count, | |
| recent_tts_votes=recent_tts_votes, | |
| recent_conv_votes=recent_conv_votes, | |
| hourly_data=json.dumps(hourly_data) | |
| ) |