EdSummariser / static /analytics.js
LiamKhoaLe's picture
Upd analytics
83c32ef
// ────────────────────────────── static/analytics.js ──────────────────────────────
(function() {
// DOM elements
const analyticsSection = document.getElementById('analytics-section');
const analyticsPeriod = document.getElementById('analytics-period');
const refreshAnalytics = document.getElementById('refresh-analytics');
const debugAnalytics = document.getElementById('debug-analytics');
const modelUsageChart = document.getElementById('model-usage-chart');
const agentUsageChart = document.getElementById('agent-usage-chart');
const dailyTrendsChart = document.getElementById('daily-trends-chart');
const usageSummary = document.getElementById('usage-summary');
// State
let currentAnalyticsData = null;
let isAnalyticsVisible = false;
// Initialize
init();
function init() {
setupEventListeners();
// Check if analytics section is already visible on page load
checkAnalyticsVisibility();
// Load analytics when section becomes visible
document.addEventListener('sectionChanged', (event) => {
console.log('[ANALYTICS] Section changed to:', event.detail.section);
if (event.detail.section === 'analytics') {
isAnalyticsVisible = true;
console.log('[ANALYTICS] Analytics section is now visible');
// Small delay to ensure DOM is ready
setTimeout(() => {
loadAnalytics();
}, 100);
} else {
isAnalyticsVisible = false;
console.log('[ANALYTICS] Analytics section is now hidden');
}
});
// Also listen for direct analytics section visibility changes
if (analyticsSection) {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'attributes' && mutation.attributeName === 'style') {
const isVisible = analyticsSection.style.display !== 'none' && analyticsSection.style.display !== '';
console.log('[ANALYTICS] Direct visibility change detected:', isVisible);
if (isVisible && !isAnalyticsVisible) {
isAnalyticsVisible = true;
setTimeout(() => {
loadAnalytics();
}, 100);
} else if (!isVisible) {
isAnalyticsVisible = false;
}
}
});
});
observer.observe(analyticsSection, { attributes: true, attributeFilter: ['style'] });
}
}
function checkAnalyticsVisibility() {
if (analyticsSection) {
const isVisible = analyticsSection.style.display !== 'none' && analyticsSection.style.display !== '';
console.log('[ANALYTICS] Initial visibility check:', isVisible);
if (isVisible) {
isAnalyticsVisible = true;
setTimeout(() => {
loadAnalytics();
}, 200);
}
}
}
function setupEventListeners() {
// Period change
if (analyticsPeriod) {
analyticsPeriod.addEventListener('change', () => {
if (isAnalyticsVisible) {
loadAnalytics();
}
});
}
// Refresh button
if (refreshAnalytics) {
refreshAnalytics.addEventListener('click', () => {
loadAnalytics();
});
}
// Debug button
if (debugAnalytics) {
debugAnalytics.addEventListener('click', () => {
console.log('[ANALYTICS] Debug button clicked');
console.log('[ANALYTICS] Current state:', {
isAnalyticsVisible,
analyticsSection: analyticsSection ? analyticsSection.style.display : 'not found',
user: window.__sb_get_user ? window.__sb_get_user() : 'not available'
});
forceLoadAnalytics();
});
}
}
async function loadAnalytics() {
console.log('[ANALYTICS] loadAnalytics called, isAnalyticsVisible:', isAnalyticsVisible);
if (!isAnalyticsVisible) {
console.log('[ANALYTICS] Analytics not visible, skipping load');
return;
}
const user = window.__sb_get_user();
console.log('[ANALYTICS] User:', user);
if (!user) {
showAnalyticsError('Please sign in to view analytics');
return;
}
const period = analyticsPeriod ? analyticsPeriod.value : '30';
console.log('[ANALYTICS] Loading analytics for period:', period);
try {
showAnalyticsLoading();
// First test if analytics system is working
console.log('[ANALYTICS] Testing analytics system...');
const testResponse = await fetch('/analytics/test');
const testData = await testResponse.json();
console.log('[ANALYTICS] Test response:', testData);
if (!testData.success) {
throw new Error(`Analytics system not working: ${testData.message}`);
}
const url = `/analytics/user?user_id=${encodeURIComponent(user.user_id)}&days=${period}`;
console.log('[ANALYTICS] Fetching from URL:', url);
const response = await fetch(url);
console.log('[ANALYTICS] Response status:', response.status);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
console.log('[ANALYTICS] Response data:', data);
if (data.success) {
currentAnalyticsData = data.data;
renderAnalytics(data.data);
} else {
throw new Error(data.message || 'Failed to load analytics');
}
} catch (error) {
console.error('Analytics loading error:', error);
showAnalyticsError(`Failed to load analytics: ${error.message}`);
}
}
function showAnalyticsLoading() {
const loadingHtml = `
<div class="analytics-loading">
<div class="spinner"></div>
<div class="loading-text">Loading analytics...</div>
</div>
`;
if (modelUsageChart) modelUsageChart.innerHTML = loadingHtml;
if (agentUsageChart) agentUsageChart.innerHTML = loadingHtml;
if (dailyTrendsChart) dailyTrendsChart.innerHTML = loadingHtml;
if (usageSummary) usageSummary.innerHTML = loadingHtml;
}
function showAnalyticsError(message) {
const errorHtml = `<div class="analytics-error">${message}</div>`;
if (modelUsageChart) modelUsageChart.innerHTML = errorHtml;
if (agentUsageChart) agentUsageChart.innerHTML = errorHtml;
if (dailyTrendsChart) dailyTrendsChart.innerHTML = errorHtml;
if (usageSummary) usageSummary.innerHTML = errorHtml;
}
function renderAnalytics(data) {
renderModelUsage(data.model_usage);
renderAgentUsage(data.agent_usage);
renderDailyTrends(data.daily_usage);
renderUsageSummary(data);
}
function renderModelUsage(modelUsage) {
if (!modelUsageChart) return;
if (!modelUsage || modelUsage.length === 0) {
modelUsageChart.innerHTML = '<div class="analytics-empty">No model usage data available</div>';
return;
}
// Sort by usage count
const sortedModels = modelUsage.sort((a, b) => b.count - a.count);
const totalUsage = sortedModels.reduce((sum, model) => sum + model.count, 0);
let html = '<div class="model-usage-list">';
sortedModels.forEach(model => {
const percentage = totalUsage > 0 ? Math.round((model.count / totalUsage) * 100) : 0;
const lastUsed = new Date(model.last_used * 1000).toLocaleDateString();
const modelName = model.model_name || model._id;
const provider = model.provider || 'unknown';
html += `
<div class="model-usage-item">
<div class="model-info">
<div class="model-name">${modelName}</div>
<div class="model-provider">${provider}</div>
</div>
<div class="model-stats">
<div class="model-count">${model.count} requests</div>
<div class="model-percentage">${percentage}%</div>
<div class="model-last-used">Last used: ${lastUsed}</div>
</div>
<div class="model-bar">
<div class="model-bar-fill" style="width: ${percentage}%"></div>
</div>
</div>
`;
});
html += '</div>';
modelUsageChart.innerHTML = html;
}
function renderAgentUsage(agentUsage) {
if (!agentUsageChart) return;
if (!agentUsage || agentUsage.length === 0) {
agentUsageChart.innerHTML = '<div class="analytics-empty">No agent usage data available</div>';
return;
}
// Sort by usage count
const sortedAgents = agentUsage.sort((a, b) => b.count - a.count);
const totalUsage = sortedAgents.reduce((sum, agent) => sum + agent.count, 0);
let html = '<div class="agent-usage-list">';
sortedAgents.forEach(agent => {
const percentage = totalUsage > 0 ? Math.round((agent.count / totalUsage) * 100) : 0;
const lastUsed = new Date(agent.last_used * 1000).toLocaleDateString();
const actions = agent.actions ? agent.actions.join(', ') : 'N/A';
html += `
<div class="agent-usage-item">
<div class="agent-info">
<div class="agent-name">${agent._id}</div>
<div class="agent-actions">Actions: ${actions}</div>
</div>
<div class="agent-stats">
<div class="agent-count">${agent.count} requests</div>
<div class="agent-percentage">${percentage}%</div>
<div class="agent-last-used">Last used: ${lastUsed}</div>
</div>
<div class="agent-bar">
<div class="agent-bar-fill" style="width: ${percentage}%"></div>
</div>
</div>
`;
});
html += '</div>';
agentUsageChart.innerHTML = html;
}
function renderDailyTrends(dailyUsage) {
if (!dailyTrendsChart) return;
if (!dailyUsage || dailyUsage.length === 0) {
dailyTrendsChart.innerHTML = '<div class="analytics-empty">No daily usage data available</div>';
return;
}
// Sort by date
const sortedDaily = dailyUsage.sort((a, b) => {
const dateA = new Date(a._id.year, a._id.month - 1, a._id.day);
const dateB = new Date(b._id.year, b._id.month - 1, b._id.day);
return dateA - dateB;
});
const maxUsage = Math.max(...sortedDaily.map(d => d.total_requests));
let html = '<div class="daily-trends-chart">';
sortedDaily.forEach(day => {
const date = new Date(day._id.year, day._id.month - 1, day._id.day);
const dateStr = date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
const height = maxUsage > 0 ? Math.max(10, (day.total_requests / maxUsage) * 100) : 10;
html += `
<div class="daily-bar" title="${day.total_requests} requests on ${date.toLocaleDateString()}">
<div class="daily-bar-fill" style="height: ${height}%"></div>
<div class="daily-label">${dateStr}</div>
<div class="daily-count">${day.total_requests}</div>
</div>
`;
});
html += '</div>';
dailyTrendsChart.innerHTML = html;
}
function renderUsageSummary(data) {
if (!usageSummary) return;
const totalRequests = data.total_requests || 0;
const periodDays = data.period_days || 30;
const avgPerDay = Math.round(totalRequests / periodDays * 10) / 10;
const modelCount = data.model_usage ? data.model_usage.length : 0;
const agentCount = data.agent_usage ? data.agent_usage.length : 0;
const mostUsedModel = data.model_usage && data.model_usage.length > 0
? data.model_usage[0]
: null;
const mostUsedAgent = data.agent_usage && data.agent_usage.length > 0
? data.agent_usage[0]
: null;
let html = `
<div class="usage-summary-content">
<div class="summary-stat">
<div class="summary-value">${totalRequests}</div>
<div class="summary-label">Total Requests</div>
</div>
<div class="summary-stat">
<div class="summary-value">${avgPerDay}</div>
<div class="summary-label">Avg per Day</div>
</div>
<div class="summary-stat">
<div class="summary-value">${modelCount}</div>
<div class="summary-label">Models Used</div>
</div>
<div class="summary-stat">
<div class="summary-value">${agentCount}</div>
<div class="summary-label">Agents Used</div>
</div>
`;
if (mostUsedModel) {
html += `
<div class="summary-highlight">
<div class="highlight-label">Most Used Model:</div>
<div class="highlight-value">${mostUsedModel._id} (${mostUsedModel.count} times)</div>
</div>
`;
}
if (mostUsedAgent) {
html += `
<div class="summary-highlight">
<div class="highlight-label">Most Used Agent:</div>
<div class="highlight-value">${mostUsedAgent._id} (${mostUsedAgent.count} times)</div>
</div>
`;
}
html += '</div>';
usageSummary.innerHTML = html;
}
// Manual trigger function for debugging
function forceLoadAnalytics() {
console.log('[ANALYTICS] Force loading analytics...');
isAnalyticsVisible = true;
loadAnalytics();
}
// Expose functions for external use
window.__sb_load_analytics = loadAnalytics;
window.__sb_force_load_analytics = forceLoadAnalytics;
window.__sb_show_analytics_section = () => {
console.log('[ANALYTICS] Showing analytics section...');
if (analyticsSection) {
analyticsSection.style.display = 'block';
isAnalyticsVisible = true;
// Trigger analytics loading
setTimeout(() => {
loadAnalytics();
}, 100);
}
};
window.__sb_hide_analytics_section = () => {
console.log('[ANALYTICS] Hiding analytics section...');
if (analyticsSection) {
analyticsSection.style.display = 'none';
isAnalyticsVisible = false;
}
};
})();