ajout de stats à la page "à propos"
This commit is contained in:
parent
dced7ae0db
commit
a15d2260d1
75
about.php
75
about.php
@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once 'includes/config.php';
|
require_once 'includes/config.php';
|
||||||
require_once 'includes/DeltaConverter.php';
|
require_once 'includes/DeltaConverter.php';
|
||||||
|
require_once 'includes/Stats.php';
|
||||||
|
|
||||||
$config = Config::load();
|
$config = Config::load();
|
||||||
$about = $config['about'] ?? [
|
$about = $config['about'] ?? [
|
||||||
@ -9,6 +10,10 @@ $about = $config['about'] ?? [
|
|||||||
'background' => ''
|
'background' => ''
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Charger les statistiques
|
||||||
|
$stats = new Stats();
|
||||||
|
$siteStats = $stats->getStats();
|
||||||
|
|
||||||
// Fonction pour convertir le contenu Delta en HTML
|
// Fonction pour convertir le contenu Delta en HTML
|
||||||
function deltaToHtml($content) {
|
function deltaToHtml($content) {
|
||||||
return DeltaConverter::toHtml($content);
|
return DeltaConverter::toHtml($content);
|
||||||
@ -38,10 +43,78 @@ function deltaToHtml($content) {
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- Contenu principal -->
|
<!-- Contenu principal -->
|
||||||
<div class="novel-content single-column">
|
<div class="novel-content">
|
||||||
<div class="novel-description">
|
<div class="novel-description">
|
||||||
<?= deltaToHtml($about['content']) ?>
|
<?= deltaToHtml($about['content']) ?>
|
||||||
</div>
|
</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>
|
||||||
|
|
||||||
<div class="back-to-home">
|
<div class="back-to-home">
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
/* Import d'autres fichiers CSS */
|
||||||
|
@import 'stats.css';
|
||||||
|
|
||||||
/* Import Google Fonts */
|
/* Import Google Fonts */
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Parisienne&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Parisienne&display=swap');
|
||||||
|
|
||||||
|
73
assets/css/stats.css
Normal file
73
assets/css/stats.css
Normal 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
118
includes/Stats.php
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user