186 lines
4.7 KiB
HTML
186 lines
4.7 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}{{ page_title }} — HF Daily Papers{% endblock %}
|
|
|
|
{% block content %}
|
|
<section class="trends-page">
|
|
<h1>趋势看板</h1>
|
|
|
|
<div class="charts-grid">
|
|
{# 按日论文数量折线图 #}
|
|
<div class="chart-card">
|
|
<h2>📅 每日论文数量(近 30 天)</h2>
|
|
<canvas id="dailyChart"></canvas>
|
|
</div>
|
|
|
|
{# 热门标签 Top 20 #}
|
|
<div class="chart-card">
|
|
<h2>🏷️ 热门标签 Top 20</h2>
|
|
<canvas id="tagsChart"></canvas>
|
|
</div>
|
|
|
|
{# Upvotes 分布 #}
|
|
<div class="chart-card">
|
|
<h2>👍 Upvotes 分布</h2>
|
|
<canvas id="upvotesChart"></canvas>
|
|
</div>
|
|
|
|
{# 总结完成率 #}
|
|
<div class="chart-card">
|
|
<h2>📝 总结完成率</h2>
|
|
<canvas id="summaryChart"></canvas>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
|
|
<script>
|
|
// 颜色配置(kami 风格墨蓝色系)
|
|
const COLORS = {
|
|
primary: '#2d5f8a',
|
|
primaryLight: 'rgba(45, 95, 138, 0.2)',
|
|
accent: '#5a9bc7',
|
|
success: '#388e3c',
|
|
warning: '#f57f17',
|
|
danger: '#c62828',
|
|
muted: '#4a4a6a',
|
|
palette: [
|
|
'#2d5f8a', '#5a9bc7', '#388e3c', '#f57f17', '#c62828',
|
|
'#7b1fa2', '#00838f', '#ef6c00', '#455a64', '#827717',
|
|
'#1565c0', '#ad1457', '#00695c', '#e65100', '#283593',
|
|
'#9e9d24', '#6a1b9a', '#00838f', '#4e342e', '#37474f',
|
|
],
|
|
};
|
|
|
|
const statsData = {{ stats | tojson }};
|
|
|
|
// 每日论文数量折线图
|
|
(function() {
|
|
const ctx = document.getElementById('dailyChart').getContext('2d');
|
|
const labels = statsData.daily_counts.map(d => d.date);
|
|
const data = statsData.daily_counts.map(d => d.count);
|
|
new Chart(ctx, {
|
|
type: 'line',
|
|
data: {
|
|
labels: labels,
|
|
datasets: [{
|
|
label: '论文数',
|
|
data: data,
|
|
borderColor: COLORS.primary,
|
|
backgroundColor: COLORS.primaryLight,
|
|
fill: true,
|
|
tension: 0.3,
|
|
pointRadius: 3,
|
|
pointHoverRadius: 6,
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
plugins: { legend: { display: false } },
|
|
scales: {
|
|
x: { ticks: { maxTicksLimit: 10, font: { size: 11 } } },
|
|
y: { beginAtZero: true, ticks: { stepSize: 1 } },
|
|
}
|
|
}
|
|
});
|
|
})();
|
|
|
|
// 热门标签柱状图
|
|
(function() {
|
|
const ctx = document.getElementById('tagsChart').getContext('2d');
|
|
const labels = statsData.top_tags.map(d => d.tag);
|
|
const data = statsData.top_tags.map(d => d.count);
|
|
new Chart(ctx, {
|
|
type: 'bar',
|
|
data: {
|
|
labels: labels,
|
|
datasets: [{
|
|
label: '论文数',
|
|
data: data,
|
|
backgroundColor: COLORS.palette.slice(0, data.length),
|
|
borderRadius: 4,
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
indexAxis: 'y',
|
|
plugins: { legend: { display: false } },
|
|
scales: {
|
|
x: { beginAtZero: true, ticks: { stepSize: 1 } },
|
|
}
|
|
}
|
|
});
|
|
})();
|
|
|
|
// Upvotes 分布
|
|
(function() {
|
|
const ctx = document.getElementById('upvotesChart').getContext('2d');
|
|
const labels = statsData.upvotes_dist.map(d => d.range);
|
|
const data = statsData.upvotes_dist.map(d => d.count);
|
|
new Chart(ctx, {
|
|
type: 'bar',
|
|
data: {
|
|
labels: labels,
|
|
datasets: [{
|
|
label: '论文数',
|
|
data: data,
|
|
backgroundColor: COLORS.accent,
|
|
borderRadius: 4,
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
plugins: { legend: { display: false } },
|
|
scales: {
|
|
y: { beginAtZero: true, ticks: { stepSize: 1 } },
|
|
}
|
|
}
|
|
});
|
|
})();
|
|
|
|
// 总结完成率环形图
|
|
(function() {
|
|
const ctx = document.getElementById('summaryChart').getContext('2d');
|
|
const statusLabels = {
|
|
'done': '已完成',
|
|
'pending': '待总结',
|
|
'processing': '总结中',
|
|
'failed': '失败',
|
|
'permanent_failure': '永久失败',
|
|
'none': '未开始',
|
|
};
|
|
const statusColors = {
|
|
'done': COLORS.success,
|
|
'pending': COLORS.warning,
|
|
'processing': COLORS.primary,
|
|
'failed': COLORS.danger,
|
|
'permanent_failure': '#b71c1c',
|
|
'none': '#bdbdbd',
|
|
};
|
|
const labels = statsData.summary_completion.map(d => statusLabels[d.status] || d.status);
|
|
const data = statsData.summary_completion.map(d => d.count);
|
|
const colors = statsData.summary_completion.map(d => statusColors[d.status] || COLORS.muted);
|
|
new Chart(ctx, {
|
|
type: 'doughnut',
|
|
data: {
|
|
labels: labels,
|
|
datasets: [{
|
|
data: data,
|
|
backgroundColor: colors,
|
|
borderWidth: 2,
|
|
borderColor: '#fff',
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
plugins: {
|
|
legend: { position: 'bottom', labels: { padding: 12 } },
|
|
}
|
|
}
|
|
});
|
|
})();
|
|
</script>
|
|
{% endblock %}
|