Files
meshcore-bot/modules/web_viewer/templates/stats.html
agessaman acd17621a6 Enhance RepeaterManager with daily advertisement tracking and statistics
- Added a new 'daily_stats' table for tracking daily advertisement statistics.
- Implemented methods to track daily advertisement counts and retrieve statistics over specified date ranges.
- Updated existing advertisement tracking logic to utilize the new daily statistics.
- Modified web viewer to display advertisement metrics using the new daily tracking data.
- Improved path command logic to prioritize database queries over API cache for path decoding.
2025-10-24 22:27:25 -07:00

333 lines
11 KiB
HTML

{% extends "base.html" %}
{% block title %}Statistics - MeshCore Bot Data Viewer{% endblock %}
{% block content %}
<div class="row">
<div class="col-12">
<h1 class="mb-4">
<i class="fas fa-chart-bar"></i> Statistics
</h1>
</div>
</div>
<!-- Key Metrics -->
<div class="row mb-4">
<div class="col-md-3">
<div class="card">
<div class="card-header">
<i class="fas fa-users"></i> Total Contacts
</div>
<div class="card-body">
<h3 id="total-contacts">0</h3>
<small class="text-muted">All time</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card">
<div class="card-header">
<i class="fas fa-comments"></i> Total Adverts
</div>
<div class="card-body">
<h3 id="total-adverts">0</h3>
<small class="text-muted">All time</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card">
<div class="card-header">
<i class="fas fa-clock"></i> Recent Activity
</div>
<div class="card-body">
<h3 id="recent-activity">0</h3>
<small class="text-muted">Last 24 hours</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card">
<div class="card-header">
<i class="fas fa-chart-line"></i> Growth Rate
</div>
<div class="card-body">
<h3 id="growth-rate">0%</h3>
<small class="text-muted">Daily growth</small>
</div>
</div>
</div>
</div>
<!-- Activity Charts -->
<div class="row mb-4">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<i class="fas fa-chart-pie"></i> Activity Distribution
</div>
<div class="card-body">
<div id="activity-chart">
<div class="loading">Loading activity chart...</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<i class="fas fa-chart-line"></i> Usage Trends
</div>
<div class="card-body">
<div id="trends-chart">
<div class="loading">Loading trends chart...</div>
</div>
</div>
</div>
</div>
</div>
<!-- Detailed Statistics -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<i class="fas fa-table"></i> Detailed Statistics
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<h6>Contact Statistics</h6>
<ul class="list-unstyled" id="contact-stats">
<li class="loading">Loading contact statistics...</li>
</ul>
</div>
<div class="col-md-6">
<h6>Advert Statistics</h6>
<ul class="list-unstyled" id="advert-stats">
<li class="loading">Loading advert statistics...</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- System Performance -->
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<i class="fas fa-server"></i> System Performance
</div>
<div class="card-body">
<div id="system-performance">
<div class="loading">Loading system performance...</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<i class="fas fa-cog"></i> Configuration
</div>
<div class="card-body">
<div id="system-config">
<div class="loading">Loading system configuration...</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
class ModernStatsManager {
constructor() {
this.statsData = {};
this.initializeStats();
}
async initializeStats() {
await this.loadStatsData();
this.setupEventHandlers();
this.updateStatistics();
this.renderCharts();
this.renderDetailedStats();
this.renderSystemInfo();
}
async loadStatsData() {
try {
const response = await fetch('/api/stats');
const data = await response.json();
if (data.error) {
this.showError('Failed to load statistics: ' + data.error);
return;
}
this.statsData = data;
} catch (error) {
console.error('Error loading statistics:', error);
this.showError('Failed to load statistics: ' + error.message);
}
}
setupEventHandlers() {
// Auto-refresh every 30 seconds
setInterval(() => {
this.loadStatsData().then(() => {
this.updateStatistics();
this.renderCharts();
this.renderDetailedStats();
this.renderSystemInfo();
});
}, 30000);
}
updateStatistics() {
document.getElementById('total-contacts').textContent = this.statsData.contact_count || 0;
document.getElementById('total-adverts').textContent = this.statsData.total_advertisements || 0;
document.getElementById('recent-activity').textContent = this.statsData.recent_contacts_24h || 0;
// Calculate growth rate
const recentContacts = this.statsData.recent_contacts_24h || 0;
const totalContacts = this.statsData.contact_count || 0;
const growthRate = totalContacts > 0 ? Math.round((recentContacts / totalContacts) * 100) : 0;
document.getElementById('growth-rate').textContent = growthRate + '%';
}
renderCharts() {
// Activity distribution chart
const activityChart = document.getElementById('activity-chart');
const recentContacts = this.statsData.recent_contacts_24h || 0;
const recentAdverts = this.statsData.advertisements_24h || 0;
const total = recentContacts + recentAdverts;
if (total > 0) {
const contactsPercent = Math.round((recentContacts / total) * 100);
const advertsPercent = Math.round((recentAdverts / total) * 100);
activityChart.innerHTML = `
<div class="row">
<div class="col-6">
<div class="text-center">
<h4 class="text-primary">${contactsPercent}%</h4>
<small class="text-muted">Contacts</small>
</div>
</div>
<div class="col-6">
<div class="text-center">
<h4 class="text-warning">${advertsPercent}%</h4>
<small class="text-muted">Adverts</small>
</div>
</div>
</div>
`;
} else {
activityChart.innerHTML = '<div class="text-muted text-center">No recent activity</div>';
}
// Trends chart
const trendsChart = document.getElementById('trends-chart');
const growthRate = this.statsData.contact_count > 0 ?
Math.round(((this.statsData.recent_contacts_24h || 0) / this.statsData.contact_count) * 100) : 0;
trendsChart.innerHTML = `
<div class="text-center">
<h4 class="text-${growthRate > 0 ? 'success' : 'secondary'}">${growthRate}%</h4>
<small class="text-muted">Daily growth rate</small>
</div>
`;
}
renderDetailedStats() {
// Contact statistics
const contactStats = document.getElementById('contact-stats');
contactStats.innerHTML = `
<li><strong>Total Contacts:</strong> ${this.statsData.contact_count || 0}</li>
<li><strong>Recent (24h):</strong> ${this.statsData.recent_contacts_24h || 0}</li>
<li><strong>Growth Rate:</strong> ${this.calculateGrowthRate()}%</li>
<li><strong>Last Updated:</strong> ${new Date().toLocaleString()}</li>
`;
// Advert statistics
const advertStats = document.getElementById('advert-stats');
advertStats.innerHTML = `
<li><strong>Total Adverts:</strong> ${this.statsData.total_advertisements || 0}</li>
<li><strong>Recent (24h):</strong> ${this.statsData.advertisements_24h || 0}</li>
<li><strong>Active Users:</strong> ${this.statsData.active_users_24h || 0}</li>
<li><strong>Last Updated:</strong> ${new Date().toLocaleString()}</li>
`;
}
renderSystemInfo() {
// System performance
const systemPerformance = document.getElementById('system-performance');
systemPerformance.innerHTML = `
<ul class="list-unstyled">
<li><strong>Connected Clients:</strong> ${this.statsData.connected_clients || 0}</li>
<li><strong>Database Status:</strong> <span class="badge bg-success">Healthy</span></li>
<li><strong>Last Activity:</strong> ${new Date().toLocaleString()}</li>
<li><strong>Uptime:</strong> ${this.calculateUptime()}</li>
</ul>
`;
// System configuration
const systemConfig = document.getElementById('system-config');
systemConfig.innerHTML = `
<ul class="list-unstyled">
<li><strong>Version:</strong> Modern v2.0</li>
<li><strong>Flask-SocketIO:</strong> 5.x</li>
<li><strong>Database:</strong> SQLite</li>
<li><strong>Real-time:</strong> <span class="badge bg-success">Enabled</span></li>
</ul>
`;
}
calculateGrowthRate() {
const recentContacts = this.statsData.recent_contacts_24h || 0;
const totalContacts = this.statsData.contact_count || 0;
return totalContacts > 0 ? Math.round((recentContacts / totalContacts) * 100) : 0;
}
calculateUptime() {
// This would be calculated based on actual system uptime
return 'Unknown';
}
showError(message) {
const errorDiv = document.createElement('div');
errorDiv.className = 'error';
errorDiv.textContent = message;
const content = document.querySelector('.container-fluid');
if (content) {
content.insertBefore(errorDiv, content.firstChild);
setTimeout(() => {
if (errorDiv.parentNode) {
errorDiv.parentNode.removeChild(errorDiv);
}
}, 5000);
}
}
}
// Initialize stats manager when page loads
document.addEventListener('DOMContentLoaded', () => {
window.statsManager = new ModernStatsManager();
});
</script>
{% endblock %}