diff --git a/src/Controller/WikiController.php b/src/Controller/WikiController.php index 26b1fda..4cec428 100644 --- a/src/Controller/WikiController.php +++ b/src/Controller/WikiController.php @@ -44,6 +44,49 @@ class WikiController extends AbstractController 'json_exists' => file_exists($outdatedPagesFile) ]); } + + /** + * Displays the evolution of page rankings over time + */ + #[Route('/wiki/rankings', name: 'app_admin_wiki_rankings')] + public function pageRankings(): Response + { + $rankingsFile = $this->getParameter('kernel.project_dir') . '/wiki_compare/page_rankings.json'; + + $timestamps = []; + $pages = []; + $globalMetrics = []; + $lastUpdated = null; + + if (file_exists($rankingsFile)) { + // Load the rankings data + try { + $rankingsData = json_decode(file_get_contents($rankingsFile), true); + + if (json_last_error() === JSON_ERROR_NONE) { + $timestamps = $rankingsData['timestamps'] ?? []; + $pages = $rankingsData['pages'] ?? []; + $globalMetrics = $rankingsData['global_metrics'] ?? []; + + // Get the last timestamp as last_updated + if (!empty($timestamps)) { + $lastUpdated = end($timestamps); + } + } + } catch (\Exception $e) { + // Log the error + error_log("Error loading rankings data: " . $e->getMessage()); + } + } + + return $this->render('admin/wiki_rankings.html.twig', [ + 'timestamps' => $timestamps, + 'pages' => $pages, + 'global_metrics' => $globalMetrics, + 'last_updated' => $lastUpdated, + 'json_exists' => file_exists($rankingsFile) + ]); + } /** * Detects incorrect heading hierarchies in a list of sections * For example, h4 directly under h2 without h3 in between @@ -1306,6 +1349,9 @@ EOT; $detailedComparison = null; $mediaDiff = 0; $historyData = null; + $prevPage = null; + $nextPage = null; + $stalenessDistribution = null; if (file_exists($jsonFile)) { // Use memory-efficient approach to extract only the necessary data @@ -1376,6 +1422,91 @@ EOT; // Combine them into a single array $allPages = array_merge($regularPages, $specificPages); + + // Sort pages by staleness score (descending) + usort($allPages, function($a, $b) { + $scoreA = $a['staleness_score'] ?? 0; + $scoreB = $b['staleness_score'] ?? 0; + return $scoreB <=> $scoreA; // Descending order + }); + + // Find the current page index in the sorted array + $currentIndex = -1; + foreach ($allPages as $index => $page) { + if (isset($page['key']) && $page['key'] === $key) { + $currentIndex = $index; + break; + } + } + + // Determine previous and next pages + if ($currentIndex > 0) { + $prevPage = $allPages[$currentIndex - 1]; + } + + if ($currentIndex < count($allPages) - 1 && $currentIndex >= 0) { + $nextPage = $allPages[$currentIndex + 1]; + } + + // Create staleness score distribution data for histogram + $stalenessScores = []; + foreach ($allPages as $page) { + if (isset($page['staleness_score'])) { + $stalenessScores[] = $page['staleness_score']; + } + } + + if (!empty($stalenessScores)) { + // Calculate statistics + $min = min($stalenessScores); + $max = max($stalenessScores); + $avg = array_sum($stalenessScores) / count($stalenessScores); + $median = $this->calculateMedian($stalenessScores); + + // Create histogram bins (10 bins) + $binCount = 10; + $binSize = ($max - $min) / $binCount; + $bins = []; + $binLabels = []; + + // Initialize bins + for ($i = 0; $i < $binCount; $i++) { + $bins[$i] = 0; + $binStart = $min + ($i * $binSize); + $binEnd = $binStart + $binSize; + $binLabels[$i] = round($binStart, 1) . ' - ' . round($binEnd, 1); + } + + // Count scores in each bin + foreach ($stalenessScores as $score) { + $binIndex = min($binCount - 1, floor(($score - $min) / $binSize)); + $bins[$binIndex]++; + } + + // Find which bin the current page falls into + $currentPageScore = 0; + foreach ($allPages as $page) { + if (isset($page['key']) && $page['key'] === $key && isset($page['staleness_score'])) { + $currentPageScore = $page['staleness_score']; + break; + } + } + + $currentPageBin = min($binCount - 1, floor(($currentPageScore - $min) / $binSize)); + + $stalenessDistribution = [ + 'scores' => $stalenessScores, + 'min' => $min, + 'max' => $max, + 'avg' => $avg, + 'median' => $median, + 'bins' => $bins, + 'binLabels' => $binLabels, + 'currentPageScore' => $currentPageScore, + 'currentPageBin' => $currentPageBin, + 'totalPages' => count($stalenessScores) + ]; + } // Find the page with the matching key foreach ($allPages as $page) { @@ -1792,7 +1923,10 @@ EOT; 'fr_sections' => $frSections, 'en_links' => $enLinks, 'fr_links' => $frLinks, - 'history_data' => $historyData + 'history_data' => $historyData, + 'prev_page' => $prevPage, + 'next_page' => $nextPage, + 'staleness_distribution' => $stalenessDistribution ]); } @@ -2207,4 +2341,30 @@ EOT; { return $this->extractJsonArrayByKey($filePath, 'specific_pages', $maxPages); } + + /** + * Calculate the median value of an array of numbers + * + * @param array $array Array of numbers + * @return float The median value + */ + private function calculateMedian(array $array): float + { + sort($array); + $count = count($array); + + if ($count === 0) { + return 0; + } + + $middle = floor($count / 2); + + if ($count % 2 === 0) { + // Even number of elements, average the two middle values + return ($array[$middle - 1] + $array[$middle]) / 2; + } else { + // Odd number of elements, return the middle value + return $array[$middle]; + } + } } \ No newline at end of file diff --git a/templates/admin/_wiki_navigation.html.twig b/templates/admin/_wiki_navigation.html.twig index faa497c..5081f7d 100644 --- a/templates/admin/_wiki_navigation.html.twig +++ b/templates/admin/_wiki_navigation.html.twig @@ -56,6 +56,11 @@ Scores de décrépitude +
{% if en_page.is_specific_page is defined and en_page.is_specific_page %} Comparaison détaillée des pages wiki en français et en anglais pour "{{ key }}". @@ -159,6 +181,63 @@
Date | +Score de décrépitude | +Différence de mots | +Différence de sections | +Différence de liens | +Différence d'images | +
---|---|---|---|---|---|
{{ entry.date }} | +
+
+ {% set score = entry.metrics.staleness_score %}
+ {% set score_class = score > 70 ? 'bg-danger' : (score > 40 ? 'bg-warning' : 'bg-success') %}
+
+
+ |
+ {{ entry.metrics.word_diff }} | +{{ entry.metrics.section_diff }} | +{{ entry.metrics.link_diff }} | +{{ entry.metrics.media_diff }} | +
Section anglaise | +Section française | +
---|---|
+ h{{ section.en.level|default(1) }} + {% if section.en.title is defined and section.en.title is not empty %} + {{ section.en.title }} + {% endif %} + | ++ h{{ section.fr.level|default(1) }} + {% if section.fr.title is defined and section.fr.title is not empty %} + {{ section.fr.title }} + {% endif %} + | +
Aucune section commune trouvée | +
{{ media.en.alt }}
+{{ media.fr.alt }}
+Texte du lien | +URL | +
---|---|
{{ link.text }} | ++ + {{ link.href|slice(0, 30) }}{% if link.href|length > 30 %}...{% endif %} + + | +
Aucun lien uniquement en anglais | +
Texte du lien | +URL | +
---|---|
{{ link.text }} | ++ + {{ link.href|slice(0, 30) }}{% if link.href|length > 30 %}...{% endif %} + + | +
Aucun lien uniquement en français | +
Texte EN | +URL EN | +Texte FR | +URL FR | +
---|---|---|---|
{{ link.en.text }} | ++ + {{ link.en.href|slice(0, 30) }}{% if link.en.href|length > 30 %}...{% endif %} + + | +{{ link.fr.text }} | ++ + {{ link.fr.href|slice(0, 30) }}{% if link.fr.href|length > 30 %}...{% endif %} + + | +
Aucun lien commun trouvé | +
Texte EN | #} - {#URL EN | #} - {#Texte FR | #} - {#URL FR | #} - {#||||
---|---|---|---|---|---|---|---|
{{ en_links[i].text }} | #} - {#{{ en_links[i].href|slice(0, 30) }} #} - {# ... | #} - {# {% else %} #} - {##} - {# | #} - {# {% endif %} #} - - {# {% if i < fr_links|length %} #} - {# | {{ fr_links[i].text }} | #} - {#{{ fr_links[i].href|slice(0, 30) }} #} - {# ... | #} - {# {% else %} #} - {##} - {# | #} - {# {% endif %} #} - {# |
Ce graphique montre la répartition des scores de décrépitude pour toutes les pages wiki et où se situe la page courante :
+ +Statistiques de décrépitude :
+Date | +Pages totales | +Score moyen | +Sections (moy.) | +Mots (moy.) | +Liens (moy.) | +Images (moy.) | +Catégories (moy.) | +
---|---|---|---|---|---|---|---|
{{ timestamp|date('d/m/Y') }} | +{{ global_metrics[timestamp].total_pages }} | +
+
+ {% set score = global_metrics[timestamp].avg_staleness %}
+ {% set score_class = score > 70 ? 'bg-danger' : (score > 40 ? 'bg-warning' : 'bg-success') %}
+
+
+ |
+ {{ global_metrics[timestamp].avg_sections }} | +{{ global_metrics[timestamp].avg_words }} | +{{ global_metrics[timestamp].avg_links }} | +{{ global_metrics[timestamp].avg_images }} | +{{ global_metrics[timestamp].avg_categories }} | +
Page | +Score actuel | +Évolution | +Actions | +
---|---|---|---|
{{ page.title }} | +
+ {% set latest_timestamp = timestamps|last %}
+ {% if page.metrics[latest_timestamp] is defined %}
+ {% set latest_score = page.metrics[latest_timestamp].staleness_score %}
+
+ {% set score_class = latest_score > 70 ? 'bg-danger' : (latest_score > 40 ? 'bg-warning' : 'bg-success') %}
+
+ {% else %}
+
+ N/A
+ {% endif %}
+ |
+ + + | ++ + Comparer + + | +