296 lines
8.7 KiB
Python
296 lines
8.7 KiB
Python
"""
|
|
Allegro-Primus Progress Dashboard
|
|
Main dashboard server with real-time metrics and visualization.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import json
|
|
import logging
|
|
from datetime import datetime, timedelta
|
|
from pathlib import Path
|
|
from typing import Dict, List, Any, Optional
|
|
|
|
from flask import Flask, render_template, jsonify, request, send_from_directory
|
|
from flask import Response
|
|
import threading
|
|
|
|
# Add parent directory to path for imports
|
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
|
|
# Import API blueprint
|
|
from api import api_bp, metrics_collector, knowledge_stats, issue_tracker
|
|
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Flask app
|
|
app = Flask(__name__,
|
|
template_folder='templates',
|
|
static_folder='static')
|
|
app.register_blueprint(api_bp)
|
|
|
|
# Configuration
|
|
DASHBOARD_PORT = int(os.getenv('AP_DASHBOARD_PORT', 8080))
|
|
DASHBOARD_HOST = os.getenv('AP_DASHBOARD_HOST', '0.0.0.0')
|
|
|
|
# Paths
|
|
BASE_DIR = Path("/root/wizards/allegro-primus")
|
|
JOURNAL_DIR = BASE_DIR / ".journal"
|
|
|
|
|
|
class DashboardData:
|
|
"""Centralized dashboard data management."""
|
|
|
|
def __init__(self):
|
|
self._cache = {}
|
|
self._cache_time = None
|
|
self._cache_ttl = 30 # seconds
|
|
|
|
def get_overview(self) -> Dict[str, Any]:
|
|
"""Get dashboard overview data."""
|
|
return {
|
|
"metrics": metrics_collector.get_current_metrics(),
|
|
"performance": metrics_collector.get_performance_metrics(),
|
|
"issues": issue_tracker.get_issue_stats(),
|
|
"knowledge": knowledge_stats.get_stats(),
|
|
"model_comparison": metrics_collector.get_model_comparison(),
|
|
"timestamp": datetime.now().isoformat()
|
|
}
|
|
|
|
def get_charts_data(self) -> Dict[str, Any]:
|
|
"""Get data for charts."""
|
|
time_series = metrics_collector.get_time_series(30)
|
|
|
|
return {
|
|
"time_series": time_series,
|
|
"success_rate_trend": [
|
|
{"date": t['date'], "rate": t['success'] / (t['success'] + t['failed']) * 100
|
|
if (t['success'] + t['failed']) > 0 else 0}
|
|
for t in time_series
|
|
],
|
|
"response_time_trend": [
|
|
{"date": t['date'], "time": t['avg_response_time']}
|
|
for t in time_series
|
|
],
|
|
"task_volume": [
|
|
{"date": t['date'], "tasks": t['total_tasks']}
|
|
for t in time_series
|
|
]
|
|
}
|
|
|
|
|
|
dashboard_data = DashboardData()
|
|
|
|
|
|
# Routes
|
|
@app.route('/')
|
|
def index():
|
|
"""Main dashboard page."""
|
|
overview = dashboard_data.get_overview()
|
|
charts_data = dashboard_data.get_charts_data()
|
|
|
|
return render_template('index.html',
|
|
overview=overview,
|
|
charts_data=charts_data,
|
|
now=datetime.now())
|
|
|
|
|
|
@app.route('/metrics')
|
|
def metrics_page():
|
|
"""Detailed metrics page."""
|
|
metrics = {
|
|
"current": metrics_collector.get_current_metrics(),
|
|
"performance": metrics_collector.get_performance_metrics(),
|
|
"model_comparison": metrics_collector.get_model_comparison(),
|
|
"time_series": metrics_collector.get_time_series(30)
|
|
}
|
|
|
|
return render_template('metrics.html', metrics=metrics)
|
|
|
|
|
|
@app.route('/journal')
|
|
def journal_page():
|
|
"""Journal entries page."""
|
|
page = request.args.get('page', 1, type=int)
|
|
per_page = request.args.get('per_page', 20, type=int)
|
|
|
|
entries = metrics_collector.journal_entries
|
|
total = len(entries)
|
|
|
|
# Sort by timestamp descending
|
|
sorted_entries = sorted(entries, key=lambda x: x.get('timestamp', ''), reverse=True)
|
|
|
|
# Paginate
|
|
start = (page - 1) * per_page
|
|
end = start + per_page
|
|
paginated_entries = sorted_entries[start:end]
|
|
|
|
total_pages = (total + per_page - 1) // per_page
|
|
|
|
return render_template('journal.html',
|
|
entries=paginated_entries,
|
|
page=page,
|
|
per_page=per_page,
|
|
total=total,
|
|
total_pages=total_pages)
|
|
|
|
|
|
@app.route('/issues')
|
|
def issues_page():
|
|
"""Issues page."""
|
|
issues = issue_tracker.get_issues()
|
|
stats = issue_tracker.get_issue_stats()
|
|
|
|
return render_template('issues.html',
|
|
issues=issues,
|
|
stats=stats)
|
|
|
|
|
|
@app.route('/knowledge')
|
|
def knowledge_page():
|
|
"""Knowledge base page."""
|
|
stats = knowledge_stats.get_stats()
|
|
|
|
return render_template('knowledge.html', stats=stats)
|
|
|
|
|
|
@app.route('/reports')
|
|
def reports_page():
|
|
"""Reports page."""
|
|
from reports import ReportGenerator
|
|
|
|
generator = ReportGenerator()
|
|
|
|
# Generate all reports
|
|
daily = generator.generate_daily_report()
|
|
weekly = generator.generate_weekly_report()
|
|
issues_report = generator.generate_issues_report()
|
|
knowledge_report = generator.generate_knowledge_report()
|
|
|
|
return render_template('reports.html',
|
|
daily=daily,
|
|
weekly=weekly,
|
|
issues=issues_report,
|
|
knowledge=knowledge_report)
|
|
|
|
|
|
@app.route('/report/<report_type>')
|
|
def view_report(report_type):
|
|
"""View specific report."""
|
|
from reports import ReportGenerator
|
|
|
|
generator = ReportGenerator()
|
|
|
|
if report_type == 'daily':
|
|
report = generator.generate_daily_report()
|
|
elif report_type == 'weekly':
|
|
report = generator.generate_weekly_report()
|
|
elif report_type == 'issues':
|
|
report = generator.generate_issues_report()
|
|
elif report_type == 'knowledge':
|
|
report = generator.generate_knowledge_report()
|
|
elif report_type == 'improvement':
|
|
report = generator.generate_self_improvement_report()
|
|
else:
|
|
return "Report not found", 404
|
|
|
|
return render_template('report.html',
|
|
report=report,
|
|
report_type=report_type)
|
|
|
|
|
|
@app.route('/export/<format>')
|
|
def export_data(format):
|
|
"""Export dashboard data."""
|
|
if format == 'json':
|
|
data = dashboard_data.get_overview()
|
|
return Response(
|
|
json.dumps(data, indent=2, default=str),
|
|
mimetype='application/json',
|
|
headers={'Content-Disposition': f'attachment; filename=ap_dashboard_{datetime.now().strftime("%Y%m%d")}.json'}
|
|
)
|
|
elif format == 'csv':
|
|
import csv
|
|
import io
|
|
|
|
output = io.StringIO()
|
|
writer = csv.writer(output)
|
|
writer.writerow(['Timestamp', 'Task', 'Result', 'Success', 'Response Time (ms)'])
|
|
|
|
for entry in metrics_collector.journal_entries:
|
|
writer.writerow([
|
|
entry.get('timestamp'),
|
|
entry.get('task'),
|
|
entry.get('result'),
|
|
entry.get('success'),
|
|
entry.get('response_time_ms')
|
|
])
|
|
|
|
return Response(
|
|
output.getvalue(),
|
|
mimetype='text/csv',
|
|
headers={'Content-Disposition': f'attachment; filename=ap_journal_{datetime.now().strftime("%Y%m%d")}.csv'}
|
|
)
|
|
|
|
return "Invalid format", 400
|
|
|
|
|
|
# Static files
|
|
@app.route('/static/<path:filename>')
|
|
def serve_static(filename):
|
|
"""Serve static files."""
|
|
return send_from_directory('static', filename)
|
|
|
|
|
|
# Error handlers
|
|
@app.errorhandler(404)
|
|
def not_found(e):
|
|
return render_template('error.html', error="Page not found"), 404
|
|
|
|
|
|
@app.errorhandler(500)
|
|
def server_error(e):
|
|
return render_template('error.html', error="Internal server error"), 500
|
|
|
|
|
|
# Context processors
|
|
@app.context_processor
|
|
def inject_globals():
|
|
"""Inject global variables into templates."""
|
|
return {
|
|
'app_name': 'Allegro-Primus Dashboard',
|
|
'app_version': '1.0.0',
|
|
'current_year': datetime.now().year
|
|
}
|
|
|
|
|
|
def run_dashboard(host: str = None, port: int = None, debug: bool = False):
|
|
"""Run the dashboard server."""
|
|
host = host or DASHBOARD_HOST
|
|
port = port or DASHBOARD_PORT
|
|
|
|
logger.info(f"Starting Allegro-Primus Dashboard on {host}:{port}")
|
|
|
|
try:
|
|
app.run(host=host, port=port, debug=debug, threaded=True)
|
|
except Exception as e:
|
|
logger.error(f"Failed to start dashboard: {e}")
|
|
raise
|
|
|
|
|
|
if __name__ == '__main__':
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(description='Allegro-Primus Dashboard')
|
|
parser.add_argument('--host', default=DASHBOARD_HOST, help='Host to bind to')
|
|
parser.add_argument('--port', type=int, default=DASHBOARD_PORT, help='Port to bind to')
|
|
parser.add_argument('--debug', action='store_true', help='Enable debug mode')
|
|
|
|
args = parser.parse_args()
|
|
|
|
run_dashboard(args.host, args.port, args.debug)
|