251 lines
7.3 KiB
PHP
251 lines
7.3 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
require PUREBLOG_BASE_PATH . '/includes/header.php';
|
||
|
|
render_masthead_layout($config, ['page' => null]);
|
||
|
|
|
||
|
|
?>
|
||
|
|
|
||
|
|
<main>
|
||
|
|
<h1> Stats </h1>
|
||
|
|
<?php
|
||
|
|
|
||
|
|
$publishedPosts = array_values(array_filter(get_all_posts(true), static fn(array $post): bool => ($post['status'] ?? 'draft') === 'published'));
|
||
|
|
$publishedCount = count($publishedPosts);
|
||
|
|
$tz = site_timezone_object($config);
|
||
|
|
$now = new DateTimeImmutable('now', $tz);
|
||
|
|
$currentYear = (int) $now->format('Y');
|
||
|
|
|
||
|
|
$publishedThisYear = 0;
|
||
|
|
$totalWordsThisYear = 0;
|
||
|
|
$tagCounts = [];
|
||
|
|
$tagCountsThisYear = [];
|
||
|
|
$tagDisplayNames = [];
|
||
|
|
$totalWords = 0;
|
||
|
|
$allTimeMonthCounts = array_fill(1, 12, 0);
|
||
|
|
$allTimeDayCounts = array_fill(1, 7, 0); // 1=Mon … 7=Sun (ISO 8601)
|
||
|
|
$yearCounts = [];
|
||
|
|
|
||
|
|
// Build rolling 12-month chart slots (oldest first)
|
||
|
|
$chartMonths = [];
|
||
|
|
$firstOfMonth = new DateTimeImmutable($now->format('Y-m-01'), $tz);
|
||
|
|
for ($i = 11; $i >= 0; $i--) {
|
||
|
|
$dt = $firstOfMonth->modify("-{$i} months");
|
||
|
|
$month = (int) $dt->format('n');
|
||
|
|
$chartMonths[] = [
|
||
|
|
'year' => (int) $dt->format('Y'),
|
||
|
|
'month' => $month,
|
||
|
|
'label' => t('date.months_short.' . ($month - 1)),
|
||
|
|
'count' => 0,
|
||
|
|
];
|
||
|
|
}
|
||
|
|
$chartIndex = [];
|
||
|
|
foreach ($chartMonths as $idx => $cm) {
|
||
|
|
$chartIndex[$cm['year'] . '-' . $cm['month']] = $idx;
|
||
|
|
}
|
||
|
|
$recentCutoff = $firstOfMonth->modify('-11 months')->getTimestamp();
|
||
|
|
|
||
|
|
foreach ($publishedPosts as $post) {
|
||
|
|
$content = (string) ($post['content'] ?? '');
|
||
|
|
$totalWords += str_word_count($content);
|
||
|
|
|
||
|
|
$timestamp = (int) ($post['timestamp'] ?? 0);
|
||
|
|
if ($timestamp > 0) {
|
||
|
|
$dt = (new DateTimeImmutable('@' . $timestamp))->setTimezone($tz);
|
||
|
|
$postYear = (int) $dt->format('Y');
|
||
|
|
$postMon = (int) $dt->format('n');
|
||
|
|
$postDay = (int) $dt->format('N'); // 1=Mon … 7=Sun
|
||
|
|
|
||
|
|
$chartKey = $postYear . '-' . $postMon;
|
||
|
|
if (isset($chartIndex[$chartKey])) {
|
||
|
|
$chartMonths[$chartIndex[$chartKey]]['count']++;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($timestamp >= $recentCutoff) {
|
||
|
|
$publishedThisYear++;
|
||
|
|
$totalWordsThisYear += str_word_count($content);
|
||
|
|
}
|
||
|
|
|
||
|
|
$allTimeMonthCounts[$postMon]++;
|
||
|
|
$allTimeDayCounts[$postDay]++;
|
||
|
|
$yearCounts[$postYear] = ($yearCounts[$postYear] ?? 0) + 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
$tags = $post['tags'] ?? [];
|
||
|
|
if (!is_array($tags)) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
foreach ($tags as $tag) {
|
||
|
|
$name = trim((string) $tag);
|
||
|
|
if ($name !== '') {
|
||
|
|
$slug = normalize_tag($name);
|
||
|
|
$tagDisplayNames[$slug] ??= $name;
|
||
|
|
$tagCounts[$slug] = ($tagCounts[$slug] ?? 0) + 1;
|
||
|
|
if ($timestamp >= $recentCutoff) {
|
||
|
|
$tagCountsThisYear[$slug] = ($tagCountsThisYear[$slug] ?? 0) + 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
ksort($yearCounts);
|
||
|
|
$maxYearCount = !empty($yearCounts) ? max($yearCounts) : 1;
|
||
|
|
|
||
|
|
$avgWordsAllTime = $publishedCount > 0 ? (int) round($totalWords / $publishedCount) : 0;
|
||
|
|
$avgWordsThisYear = $publishedThisYear > 0 ? (int) round($totalWordsThisYear / $publishedThisYear) : 0;
|
||
|
|
$booksEquivalent = $totalWords > 0 ? round($totalWords / 80000, 1) : 0;
|
||
|
|
$booksThisYear = $totalWordsThisYear > 0 ? round($totalWordsThisYear / 80000, 1) : 0;
|
||
|
|
|
||
|
|
// Last published time
|
||
|
|
$lastTimestamp = 0;
|
||
|
|
foreach ($publishedPosts as $post) {
|
||
|
|
$ts = (int) ($post['timestamp'] ?? 0);
|
||
|
|
if ($ts > $lastTimestamp) {
|
||
|
|
$lastTimestamp = $ts;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
$lastPublishedAgo = $lastTimestamp > 0 ? relative_time($lastTimestamp) : t('admin.dashboard.no_posts_yet');
|
||
|
|
|
||
|
|
?>
|
||
|
|
</main>
|
||
|
|
<?php render_footer_layout($config, ['page' => null]); ?>
|
||
|
|
</body>
|
||
|
|
|
||
|
|
|
||
|
|
pj@laptop5:~/pureblog$ sudo docker compose up -d --force-recreate
|
||
|
|
|
||
|
|
|
||
|
|
pj@laptop5:~/pureblog$ sudo docker compose up -d --force-recreate
|
||
|
|
[+] up 1/1
|
||
|
|
|
||
|
|
|
||
|
|
pj@laptop5:~/pureblog$ sudo docker compose up -d --force-recreate
|
||
|
|
[+] up 1/1
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
pj@laptop5:~/pureblog$ sudo docker compose up -d --force-recreate
|
||
|
|
pj@laptop5:~/pureblog$
|
||
|
|
pj@laptop5:~/pureblog$ sudo docker compose up -d --force-recreate
|
||
|
|
pj@laptop5:~/pureblog$ sudo cat content/includes/stats.php
|
||
|
|
<?php 4.0s
|
||
|
|
|
||
|
|
require PUREBLOG_BASE_PATH . '/includes/header.php';
|
||
|
|
render_masthead_layout($config, ['page' => null]);
|
||
|
|
|
||
|
|
?>
|
||
|
|
|
||
|
|
<main>
|
||
|
|
<h1> Stats </h1>
|
||
|
|
<?php
|
||
|
|
|
||
|
|
$publishedPosts = array_values(array_filter(get_all_posts(true), static fn(array $post): bool => ($post['status'] ?? 'draft') === 'published'));
|
||
|
|
$publishedCount = count($publishedPosts);
|
||
|
|
$tz = site_timezone_object($config);
|
||
|
|
$now = new DateTimeImmutable('now', $tz);
|
||
|
|
$currentYear = (int) $now->format('Y');
|
||
|
|
|
||
|
|
$publishedThisYear = 0;
|
||
|
|
$totalWordsThisYear = 0;
|
||
|
|
$tagCounts = [];
|
||
|
|
$tagCountsThisYear = [];
|
||
|
|
$tagDisplayNames = [];
|
||
|
|
$totalWords = 0;
|
||
|
|
$allTimeMonthCounts = array_fill(1, 12, 0);
|
||
|
|
$allTimeDayCounts = array_fill(1, 7, 0); // 1=Mon … 7=Sun (ISO 8601)
|
||
|
|
$yearCounts = [];
|
||
|
|
|
||
|
|
// Build rolling 12-month chart slots (oldest first)
|
||
|
|
$chartMonths = [];
|
||
|
|
$firstOfMonth = new DateTimeImmutable($now->format('Y-m-01'), $tz);
|
||
|
|
for ($i = 11; $i >= 0; $i--) {
|
||
|
|
$dt = $firstOfMonth->modify("-{$i} months");
|
||
|
|
$month = (int) $dt->format('n');
|
||
|
|
$chartMonths[] = [
|
||
|
|
'year' => (int) $dt->format('Y'),
|
||
|
|
'month' => $month,
|
||
|
|
'label' => t('date.months_short.' . ($month - 1)),
|
||
|
|
'count' => 0,
|
||
|
|
];
|
||
|
|
}
|
||
|
|
$chartIndex = [];
|
||
|
|
foreach ($chartMonths as $idx => $cm) {
|
||
|
|
$chartIndex[$cm['year'] . '-' . $cm['month']] = $idx;
|
||
|
|
}
|
||
|
|
$recentCutoff = $firstOfMonth->modify('-11 months')->getTimestamp();
|
||
|
|
|
||
|
|
foreach ($publishedPosts as $post) {
|
||
|
|
$content = (string) ($post['content'] ?? '');
|
||
|
|
$totalWords += str_word_count($content);
|
||
|
|
|
||
|
|
$timestamp = (int) ($post['timestamp'] ?? 0);
|
||
|
|
if ($timestamp > 0) {
|
||
|
|
$dt = (new DateTimeImmutable('@' . $timestamp))->setTimezone($tz);
|
||
|
|
$postYear = (int) $dt->format('Y');
|
||
|
|
$postMon = (int) $dt->format('n');
|
||
|
|
$postDay = (int) $dt->format('N'); // 1=Mon … 7=Sun
|
||
|
|
|
||
|
|
$chartKey = $postYear . '-' . $postMon;
|
||
|
|
if (isset($chartIndex[$chartKey])) {
|
||
|
|
$chartMonths[$chartIndex[$chartKey]]['count']++;
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($timestamp >= $recentCutoff) {
|
||
|
|
$publishedThisYear++;
|
||
|
|
$totalWordsThisYear += str_word_count($content);
|
||
|
|
}
|
||
|
|
|
||
|
|
$allTimeMonthCounts[$postMon]++;
|
||
|
|
$allTimeDayCounts[$postDay]++;
|
||
|
|
$yearCounts[$postYear] = ($yearCounts[$postYear] ?? 0) + 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
$tags = $post['tags'] ?? [];
|
||
|
|
if (!is_array($tags)) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
foreach ($tags as $tag) {
|
||
|
|
$name = trim((string) $tag);
|
||
|
|
if ($name !== '') {
|
||
|
|
$slug = normalize_tag($name);
|
||
|
|
$tagDisplayNames[$slug] ??= $name;
|
||
|
|
$tagCounts[$slug] = ($tagCounts[$slug] ?? 0) + 1;
|
||
|
|
if ($timestamp >= $recentCutoff) {
|
||
|
|
$tagCountsThisYear[$slug] = ($tagCountsThisYear[$slug] ?? 0) + 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
ksort($yearCounts);
|
||
|
|
$maxYearCount = !empty($yearCounts) ? max($yearCounts) : 1;
|
||
|
|
|
||
|
|
$avgWordsAllTime = $publishedCount > 0 ? (int) round($totalWords / $publishedCount) : 0;
|
||
|
|
$avgWordsThisYear = $publishedThisYear > 0 ? (int) round($totalWordsThisYear / $publishedThisYear) : 0;
|
||
|
|
$booksEquivalent = $totalWords > 0 ? round($totalWords / 80000, 1) : 0;
|
||
|
|
$booksThisYear = $totalWordsThisYear > 0 ? round($totalWordsThisYear / 80000, 1) : 0;
|
||
|
|
|
||
|
|
// Last published time
|
||
|
|
$lastTimestamp = 0;
|
||
|
|
foreach ($publishedPosts as $post) {
|
||
|
|
$ts = (int) ($post['timestamp'] ?? 0);
|
||
|
|
if ($ts > $lastTimestamp) {
|
||
|
|
$lastTimestamp = $ts;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
$lastPublishedAgo = $lastTimestamp > 0 ? relative_time($lastTimestamp) : t('admin.dashboard.no_posts_yet');
|
||
|
|
|
||
|
|
?>
|
||
|
|
<div class="stats">
|
||
|
|
|
||
|
|
<h3> Published posts: </h3> <?= e(number_format($publishedCount)) ?><br>
|
||
|
|
|
||
|
|
<h3>Total words:</h3> <?= e(number_format($totalWords)) ?><br>
|
||
|
|
<br>
|
||
|
|
<h3>Average words per post:</h3> <?= e(number_format($avgWordsAllTime)) ?>
|
||
|
|
<br><h3>Book equivalents (base 80,000):</h3> <?= e(number_format($booksEquivalent, 1)) ?><br>
|
||
|
|
<h3>Last published:</h3> <?= e($lastPublishedAgo) ?></p>
|
||
|
|
</div>
|
||
|
|
</main>
|
||
|
|
<?php render_footer_layout($config, ['page' => null]); ?>
|
||
|
|
</body>
|
||
|
|
</html>
|