ajout de stats à la page "à propos"

This commit is contained in:
Esenjin 2025-02-16 22:34:33 +01:00
parent dced7ae0db
commit a15d2260d1
4 changed files with 268 additions and 1 deletions

View File

@ -1,6 +1,7 @@
<?php
require_once 'includes/config.php';
require_once 'includes/DeltaConverter.php';
require_once 'includes/Stats.php';
$config = Config::load();
$about = $config['about'] ?? [
@ -9,6 +10,10 @@ $about = $config['about'] ?? [
'background' => ''
];
// Charger les statistiques
$stats = new Stats();
$siteStats = $stats->getStats();
// Fonction pour convertir le contenu Delta en HTML
function deltaToHtml($content) {
return DeltaConverter::toHtml($content);
@ -38,10 +43,78 @@ function deltaToHtml($content) {
</header>
<!-- Contenu principal -->
<div class="novel-content single-column">
<div class="novel-content">
<div class="novel-description">
<?= deltaToHtml($about['content']) ?>
</div>
<aside class="stats-menu">
<h2>Statistiques</h2>
<ul class="stats-list">
<!-- Nombre total de romans -->
<li class="stats-item">
<div class="stats-label">Romans publiés</div>
<div class="stats-value"><?= Stats::formatNumber($siteStats['total_stories']) ?></div>
</li>
<!-- Nombre total de chapitres -->
<li class="stats-item">
<div class="stats-label">Chapitres écrits</div>
<div class="stats-value"><?= Stats::formatNumber($siteStats['total_chapters']) ?></div>
<div class="stats-detail">
Moyenne de <?= $siteStats['avg_chapters_per_story'] ?> chapitres par roman
</div>
</li>
<!-- Nombre total de mots -->
<li class="stats-item">
<div class="stats-label">Mots écrits</div>
<div class="stats-value"><?= Stats::formatNumber($siteStats['total_words']) ?></div>
<div class="stats-detail">
Moyenne de <?= Stats::formatNumber($siteStats['avg_words_per_chapter']) ?> mots par chapitre
</div>
</li>
<!-- Roman avec le plus de chapitres -->
<?php if ($siteStats['most_chapters']['story']): ?>
<li class="stats-item">
<div class="stats-label">Plus long roman</div>
<div class="stats-value"><?= Stats::formatNumber($siteStats['most_chapters']['count']) ?> chapitres</div>
<div class="stats-detail">
<a href="roman.php?id=<?= htmlspecialchars($siteStats['most_chapters']['story']['id']) ?>">
<?= htmlspecialchars($siteStats['most_chapters']['story']['title']) ?>
</a>
</div>
</li>
<?php endif; ?>
<!-- Plus long chapitre -->
<?php if ($siteStats['longest_chapter']['story']): ?>
<li class="stats-item">
<div class="stats-label">Plus long chapitre</div>
<div class="stats-value"><?= Stats::formatNumber($siteStats['longest_chapter']['words']) ?> mots</div>
<div class="stats-detail">
<a href="roman.php?id=<?= htmlspecialchars($siteStats['longest_chapter']['story']['id']) ?>">
<?= htmlspecialchars($siteStats['longest_chapter']['story']['title']) ?>
</a>
</div>
</li>
<?php endif; ?>
<!-- Dernière mise à jour -->
<?php if ($siteStats['latest_update']['story']): ?>
<li class="stats-item">
<div class="stats-label">Dernière mise à jour</div>
<div class="stats-value"><?= Stats::formatDate($siteStats['latest_update']['date']) ?></div>
<div class="stats-detail">
<a href="roman.php?id=<?= htmlspecialchars($siteStats['latest_update']['story']['id']) ?>">
<?= htmlspecialchars($siteStats['latest_update']['story']['title']) ?>
</a>
</div>
</li>
<?php endif; ?>
</ul>
</aside>
</div>
<div class="back-to-home">

View File

@ -1,3 +1,6 @@
/* Import d'autres fichiers CSS */
@import 'stats.css';
/* Import Google Fonts */
@import url('https://fonts.googleapis.com/css2?family=Parisienne&display=swap');

73
assets/css/stats.css Normal file
View File

@ -0,0 +1,73 @@
.stats-menu {
background: var(--bg-tertiary);
padding: var(--spacing-lg);
border-radius: var(--radius-md);
border: 1px solid var(--border-color);
position: sticky;
top: var(--spacing-lg);
align-self: start;
}
.stats-menu h2 {
font-size: 1.5rem;
margin-bottom: var(--spacing-md);
color: var(--text-primary);
padding-bottom: var(--spacing-sm);
border-bottom: 2px solid var(--accent-primary);
}
.stats-list {
list-style: none;
margin: 0;
padding: 0;
}
.stats-item {
margin-bottom: var(--spacing-md);
padding-bottom: var(--spacing-md);
border-bottom: 1px solid var(--border-color);
}
.stats-item:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.stats-label {
font-size: 0.9rem;
color: var(--text-secondary);
margin-bottom: var(--spacing-xs);
}
.stats-value {
font-size: 1.2rem;
color: var(--text-primary);
font-weight: bold;
}
.stats-detail {
font-size: 0.85rem;
color: var(--accent-primary);
margin-top: var(--spacing-xs);
}
/* Style pour les liens dans les stats */
.stats-detail a {
color: var(--accent-primary);
text-decoration: none;
transition: color var(--transition-fast);
}
.stats-detail a:hover {
color: var(--accent-secondary);
text-decoration: underline;
}
/* Media queries */
@media (max-width: 900px) {
.stats-menu {
position: static;
margin-top: var(--spacing-xl);
}
}

118
includes/Stats.php Normal file
View File

@ -0,0 +1,118 @@
<?php
class Stats {
private static $storiesDir = __DIR__ . '/../stories/';
private $stories = [];
private $stats = [];
public function __construct() {
$this->loadStories();
$this->calculateStats();
}
private function loadStories() {
foreach (glob(self::$storiesDir . '*.json') as $file) {
$this->stories[] = json_decode(file_get_contents($file), true);
}
}
private function calculateStats() {
$totalChapters = 0;
$totalWords = 0;
$maxChapters = 0;
$storyWithMostChapters = null;
$lastUpdate = null;
$mostRecentStory = null;
$longestChapter = [
'words' => 0,
'story' => null,
'chapter' => null
];
foreach ($this->stories as $story) {
// Compter les chapitres
$chapterCount = count($story['chapters'] ?? []);
$totalChapters += $chapterCount;
// Trouver le roman avec le plus de chapitres
if ($chapterCount > $maxChapters) {
$maxChapters = $chapterCount;
$storyWithMostChapters = $story;
}
// Trouver la dernière mise à jour
$storyUpdate = strtotime($story['updated']);
if (!$lastUpdate || $storyUpdate > $lastUpdate) {
$lastUpdate = $storyUpdate;
$mostRecentStory = $story;
}
// Compter les mots et trouver le plus long chapitre
foreach ($story['chapters'] ?? [] as $chapter) {
$content = $chapter['content'];
// Si le contenu est au format JSON (Delta)
if (is_string($content) && $this->isJson($content)) {
$content = json_decode($content, true);
$text = '';
if (isset($content['ops'])) {
foreach ($content['ops'] as $op) {
if (is_string($op['insert'])) {
$text .= $op['insert'];
}
}
}
} else {
// Si le contenu est en HTML
$text = strip_tags($content);
}
$wordCount = str_word_count(strip_tags($text));
$totalWords += $wordCount;
if ($wordCount > $longestChapter['words']) {
$longestChapter = [
'words' => $wordCount,
'story' => $story,
'chapter' => $chapter
];
}
}
}
$this->stats = [
'total_stories' => count($this->stories),
'total_chapters' => $totalChapters,
'total_words' => $totalWords,
'avg_chapters_per_story' => $this->stories ? round($totalChapters / count($this->stories), 1) : 0,
'avg_words_per_chapter' => $totalChapters ? round($totalWords / $totalChapters) : 0,
'most_chapters' => [
'story' => $storyWithMostChapters,
'count' => $maxChapters
],
'longest_chapter' => $longestChapter,
'latest_update' => [
'story' => $mostRecentStory,
'date' => $lastUpdate
]
];
}
public function getStats() {
return $this->stats;
}
private function isJson($string) {
json_decode($string);
return json_last_error() === JSON_ERROR_NONE;
}
// Méthodes utilitaires pour le formatage
public static function formatNumber($number) {
return number_format($number, 0, ',', ' ');
}
public static function formatDate($timestamp) {
if (!$timestamp) return 'Jamais';
return date('d/m/Y', $timestamp);
}
}