up stats sections

This commit is contained in:
Tykayn 2025-11-02 18:40:04 +01:00 committed by tykayn
parent c81aeb466c
commit 0ff2c68eac
3 changed files with 223 additions and 63 deletions

View file

@ -10,7 +10,8 @@ import re
import argparse import argparse
import shutil import shutil
from datetime import datetime from datetime import datetime
from collections import defaultdict from collections import defaultdict, Counter
from urllib.parse import urlparse
import locale import locale
try: try:
@ -164,6 +165,62 @@ def compter_signes(contenu, est_markdown=False):
return len(contenu_clean) return len(contenu_clean)
def extraire_domaine(url):
"""
Extrait le domaine d'une URL.
Retourne None si ce n'est pas une URL valide.
"""
try:
# Ajouter http:// si l'URL ne commence pas par http:// ou https://
if not url.startswith(('http://', 'https://')):
if url.startswith('www.'):
url = 'http://' + url
else:
return None
parsed = urlparse(url)
domain = parsed.netloc
# Supprimer www. si présent
if domain.startswith('www.'):
domain = domain[4:]
# Ignorer les URLs locales ou non valides
if not domain or domain in ['localhost', '']:
return None
return domain
except Exception:
return None
def extraire_liens(contenu, est_markdown=False):
"""
Extrait tous les liens du contenu et retourne une liste d'URLs.
"""
urls = []
if est_markdown:
# Extraire les liens markdown: [texte](url) ou ![alt](url)
liens_images = re.findall(r'!\[([^\]]*)\]\(([^\)]+)\)', contenu)
liens_autres = re.findall(r'(?<!\!)\[([^\]]+)\]\(([^\)]+)\)', contenu)
for url in [l[1] for l in liens_images]:
urls.append(url)
for url in [l[1] for l in liens_autres]:
urls.append(url)
else:
# Extraire les liens org-mode: [[url]] ou [[url][texte]]
liens = re.findall(r'\[\[([^\]]+)\](\[[^\]]+\])?\]', contenu)
for lien_match in liens:
url = lien_match[0]
urls.append(url)
return urls
def compter_liens(contenu, est_markdown=False): def compter_liens(contenu, est_markdown=False):
""" """
Compte le nombre de liens dans le contenu. Compte le nombre de liens dans le contenu.
@ -178,45 +235,43 @@ def compter_liens(contenu, est_markdown=False):
# Extensions d'images courantes # Extensions d'images courantes
extensions_images = ['.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp', '.bmp', '.ico'] extensions_images = ['.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp', '.bmp', '.ico']
if est_markdown: urls = extraire_liens(contenu, est_markdown)
# Compter les liens markdown: [texte](url) ou ![alt](url)
liens_images = re.findall(r'!\[([^\]]*)\]\(([^\)]+)\)', contenu)
liens_autres = re.findall(r'(?<!\!)\[([^\]]+)\]\(([^\)]+)\)', contenu)
for url in [l[1] for l in liens_images]: for url in urls:
is_image = any(url.lower().endswith(ext) for ext in extensions_images) or \ # Vérifier si c'est une image
'/image' in url.lower() or \ is_image = any(url.lower().endswith(ext) for ext in extensions_images) or \
'img' in url.lower() '/image' in url.lower() or \
if is_image: 'img' in url.lower()
nb_images += 1
for url in [l[1] for l in liens_autres]: if is_image:
is_image = any(url.lower().endswith(ext) for ext in extensions_images) or \ nb_images += 1
'/image' in url.lower() or \ else:
'img' in url.lower() nb_autres += 1
if is_image:
nb_images += 1
else:
nb_autres += 1
else:
# Compter les liens org-mode
liens = re.findall(r'\[\[([^\]]+)\](\[[^\]]+\])?\]', contenu)
for lien_match in liens:
url = lien_match[0]
# Vérifier si c'est une image
is_image = any(url.lower().endswith(ext) for ext in extensions_images) or \
'/image' in url.lower() or \
'img' in url.lower()
if is_image:
nb_images += 1
else:
nb_autres += 1
return nb_images, nb_autres return nb_images, nb_autres
def calculer_domaines_liens(articles):
"""
Calcule les domaines des liens web et retourne un Counter trié par occurrence décroissante.
Retourne une liste de tuples (domaine, nombre) triée par nombre décroissant.
"""
domaines_counter = Counter()
for article in articles:
urls = extraire_liens(article.get('contenu', ''), article.get('est_markdown', False))
for url in urls:
domain = extraire_domaine(url)
if domain:
domaines_counter[domain] += 1
# Trier par occurrence décroissante
domaines_tries = sorted(domaines_counter.items(), key=lambda x: x[1], reverse=True)
return domaines_tries
def analyser_article(filepath): def analyser_article(filepath):
""" """
Analyse un fichier article (org ou markdown) et retourne ses statistiques. Analyse un fichier article (org ou markdown) et retourne ses statistiques.
@ -264,7 +319,9 @@ def analyser_article(filepath):
'liens_images': nb_liens_images, 'liens_images': nb_liens_images,
'liens_autres': nb_liens_autres, 'liens_autres': nb_liens_autres,
'liens': nb_liens_images + nb_liens_autres, # Total pour compatibilité 'liens': nb_liens_images + nb_liens_autres, # Total pour compatibilité
'temps_lecture': temps_lecture 'temps_lecture': temps_lecture,
'contenu': contenu, # Contenu brut pour extraction des domaines
'est_markdown': est_markdown
} }
except Exception as e: except Exception as e:
print(f"Erreur lors de l'analyse de {filepath}: {e}") print(f"Erreur lors de l'analyse de {filepath}: {e}")
@ -730,6 +787,9 @@ def generer_statistiques_blog(blog_name, sources_dir, html_websites_dir, env, te
liens_moyen = liens_total / nb_articles_total if nb_articles_total > 0 else 0 liens_moyen = liens_total / nb_articles_total if nb_articles_total > 0 else 0
liens_images_moyen = liens_images_total / nb_articles_total if nb_articles_total > 0 else 0 liens_images_moyen = liens_images_total / nb_articles_total if nb_articles_total > 0 else 0
liens_autres_moyen = liens_autres_total / nb_articles_total if nb_articles_total > 0 else 0 liens_autres_moyen = liens_autres_total / nb_articles_total if nb_articles_total > 0 else 0
# Calculer les domaines des liens
domaines_liens = calculer_domaines_liens(articles)
signes_moyen = signes_total / nb_articles_total if nb_articles_total > 0 else 0 signes_moyen = signes_total / nb_articles_total if nb_articles_total > 0 else 0
temps_lecture_par_article = temps_lecture_total / nb_articles_total if nb_articles_total > 0 else 0 temps_lecture_par_article = temps_lecture_total / nb_articles_total if nb_articles_total > 0 else 0
@ -783,7 +843,8 @@ def generer_statistiques_blog(blog_name, sources_dir, html_websites_dir, env, te
date_gen=date_gen, date_gen=date_gen,
lecture_mots_par_minute=LECTURE_MOTS_PAR_MINUTE, lecture_mots_par_minute=LECTURE_MOTS_PAR_MINUTE,
css_path='stats.css', css_path='stats.css',
sections_stats=None # Pas de sections pour l'analyse de blog complet sections_stats=None, # Pas de sections pour l'analyse de blog complet
domaines_liens=domaines_liens
) )
# Sauvegarder le HTML # Sauvegarder le HTML
@ -1157,6 +1218,12 @@ def generer_page_combinee(blogs_data, output_file, html_websites_dir, env, objec
# Calculer les statistiques NaNoWriMo combinées pour les 3 derniers mois # Calculer les statistiques NaNoWriMo combinées pour les 3 derniers mois
stats_nanowrimo_combines = calculer_nanowrimo_combine(blogs_data, objectif_quotidien, objectif_mensuel) stats_nanowrimo_combines = calculer_nanowrimo_combine(blogs_data, objectif_quotidien, objectif_mensuel)
# Calculer les domaines combinés de tous les blogs
tous_articles = []
for blog_data in blogs_data:
tous_articles.extend(blog_data.get('articles', []))
domaines_liens_combines = calculer_domaines_liens(tous_articles) if tous_articles else []
# Générer les graphiques combinés # Générer les graphiques combinés
graphiques_combines, graph_path_combines = generer_graphiques_combines(blogs_data, html_websites_dir) graphiques_combines, graph_path_combines = generer_graphiques_combines(blogs_data, html_websites_dir)
@ -1422,6 +1489,9 @@ def generer_statistiques_fichier(filepath, html_websites_dir, env, template, dat
liens_autres_total = article['liens_autres'] liens_autres_total = article['liens_autres']
temps_lecture_total = article['temps_lecture'] temps_lecture_total = article['temps_lecture']
# Calculer les domaines des liens
domaines_liens = calculer_domaines_liens([article])
# Parser les sections si c'est un fichier org # Parser les sections si c'est un fichier org
sections_stats = None sections_stats = None
if ext == '.org': if ext == '.org':
@ -1509,7 +1579,8 @@ def generer_statistiques_fichier(filepath, html_websites_dir, env, template, dat
nb_jours_avec_articles=1, nb_jours_avec_articles=1,
nb_mois_avec_articles=1, nb_mois_avec_articles=1,
nb_annees_avec_articles=1, nb_annees_avec_articles=1,
sections_stats=sections_stats sections_stats=sections_stats,
domaines_liens=domaines_liens
) )
# Sauvegarder le HTML # Sauvegarder le HTML

View file

@ -28,7 +28,9 @@ documentaire "le clitoris ce cher inconnu" que la plupart des femmes
entre 20 et 25 ans ne savaient pas situer leur clitoris, beaucoup entre 20 et 25 ans ne savaient pas situer leur clitoris, beaucoup
croyaient que c'était la partie de leur sexe qui leur permettait croyaient que c'était la partie de leur sexe qui leur permettait
d'uriner et ignoraient tout de son rôle dans le plaisir sexuel d'uriner et ignoraient tout de son rôle dans le plaisir sexuel
féminin./[[https://qzine.fr/wp-content/uploads/2016/05/received_1198948796817047-723x1024.jpeg]]/Le mois féminin.
[[https://qzine.fr/wp-content/uploads/2016/05/received_1198948796817047-723x1024.jpeg]]/Le mois
de mai a été de mai a été
[[https://next.liberation.fr/sexe/2012/05/04/en-mai-masturbez-vous_816162][déclaré [[https://next.liberation.fr/sexe/2012/05/04/en-mai-masturbez-vous_816162][déclaré
mois de la masturbation]] en 2012, on serait donc en droit de croire que mois de la masturbation]] en 2012, on serait donc en droit de croire que
@ -49,7 +51,10 @@ branleur êtes vous ?]]  et
sextoys]] (avec des échantillons de personnes non sélectionnés) sextoys]] (avec des échantillons de personnes non sélectionnés)
indiquent pour les personnes ayant répondu, que la masturbation fait indiquent pour les personnes ayant répondu, que la masturbation fait
partie de leur quotidien. La majorité des fréquences de masturbation se partie de leur quotidien. La majorité des fréquences de masturbation se
situant entre deux fois par jour et un jour sur deux. Il reste plus situant entre deux fois par jour et un jour sur deux.
Il reste plus
facile pour tout le monde d'aborder le sujet entre amis comparé à des facile pour tout le monde d'aborder le sujet entre amis comparé à des
discussions entre membres de la famille.Il reste discussions entre membres de la famille.Il reste
difficile d'objectivement dire pourquoi le plaisir est plus dur à difficile d'objectivement dire pourquoi le plaisir est plus dur à
@ -79,10 +84,15 @@ participer au débat? Parce que bon, c'est à dire que l'anus contient de
nombreuses terminaisons nerveuses (tout comme bien d'autres organes), nombreuses terminaisons nerveuses (tout comme bien d'autres organes),
faites pour ressentir plein de choses, quel que soit votre genre. Et faites pour ressentir plein de choses, quel que soit votre genre. Et
pour celles qui jouissent de plusieurs endroits et qui n'ont pas de pour celles qui jouissent de plusieurs endroits et qui n'ont pas de
préférence? En fait si.[caption id="" préférence? En fait si.
[caption id=""
align="aligncenter" align="aligncenter"
width="640"][[https://upload.wikimedia.org/wikipedia/commons/thumb/b/bd/Sch%C3%A9ma_strucurel_l%C3%A9gend%C3%A9_du_clitoris.svg/640px-Sch%C3%A9ma_strucurel_l%C3%A9gend%C3%A9_du_clitoris.svg.png?1464106481474]] width="640"][[https://upload.wikimedia.org/wikipedia/commons/thumb/b/bd/Sch%C3%A9ma_strucurel_l%C3%A9gend%C3%A9_du_clitoris.svg/640px-Sch%C3%A9ma_strucurel_l%C3%A9gend%C3%A9_du_clitoris.svg.png?1464106481474]]
schéma de clitoris[/caption]Ce que l'on voit du schéma de clitoris[/caption]
Ce que l'on voit du
clitoris n'est que son gland, caché sous son capuchon. à moins qu'on clitoris n'est que son gland, caché sous son capuchon. à moins qu'on
s'appelle Jack l'éventreur et qu'on découpe des gens. Comme le montre le s'appelle Jack l'éventreur et qu'on découpe des gens. Comme le montre le
schéma ci contre, le clitoris englobe le vagin et se cache derrière les schéma ci contre, le clitoris englobe le vagin et se cache derrière les
@ -127,22 +137,14 @@ connaissent bien leur corps, ou si vous pensez que les femmes ont moins
d'envie de plaisir sexuel que les hommes. d'envie de plaisir sexuel que les hommes.
<<player>>
* Une erreur s'est produite.
:PROPERTIES:
:CUSTOM_ID: une-erreur-sest-produite.
:CLASS: message
:END:
Impossible d'exécuter JavaScript.
Première diffusion sur ARTE, le 16 Janvier Première diffusion sur ARTE, le 16 Janvier
2004à 22h15, dans le cadre d'une THEMA : Le 2004à 22h15, dans le cadre d'une THEMA : Le
sexedes sexe des
femmes [[https://qzine.fr/wp-content/uploads/2016/05/mamiepointG.png]](mamie femmes
pantoufle par Milk)Heureusement que mamie pantoufle
 [[https://qzine.fr/wp-content/uploads/2016/05/mamiepointG.png]](mamie
pantoufle par Milk)
Heureusement que mamie pantoufle
est là pour rétablir la vérité! est là pour rétablir la vérité!

View file

@ -270,6 +270,57 @@
{% if sections_stats %} {% if sections_stats %}
<h2>Analyse des sections</h2> <h2>Analyse des sections</h2>
{% set niveaux_vus = [] %}
{% set objectifs_text = [] %}
{% for section in sections_stats %}
{% if section.niveau not in niveaux_vus %}
{% set _ = niveaux_vus.append(section.niveau) %}
{% set _ = objectifs_text.append("Niveau " + section.niveau|string + " : " + section.objectif|int|string + " mots") %}
{% endif %}
{% endfor %}
<p style="color: #666; margin-bottom: 15px; font-size: 0.95em;">
<strong>Objectif par section :</strong> {{objectifs_text|join(" | ")}}
</p>
<style>
.tooltip-container {
position: relative;
display: inline-block;
cursor: help;
}
.tooltip-text {
visibility: hidden;
opacity: 0;
background-color: #333;
color: #fff;
text-align: left;
padding: 10px;
border-radius: 6px;
position: absolute;
z-index: 1000;
bottom: 125%;
left: 50%;
transform: translateX(-50%);
white-space: normal;
width: 250px;
font-size: 0.9em;
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
transition: opacity 0.3s, visibility 0.3s;
}
.tooltip-container:hover .tooltip-text,
.tooltip-container:focus .tooltip-text {
visibility: visible;
opacity: 1;
}
.tooltip-text::after {
content: "";
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
border: 5px solid;
border-color: #333 transparent transparent transparent;
}
</style>
<table style="width: 100%; border-collapse: collapse; margin-bottom: 30px;"> <table style="width: 100%; border-collapse: collapse; margin-bottom: 30px;">
<thead> <thead>
<tr> <tr>
@ -298,11 +349,25 @@
<td style="text-align: right; padding: 10px; border-bottom: 1px solid #eee;">{{section.mots|int|format_number}}</td> <td style="text-align: right; padding: 10px; border-bottom: 1px solid #eee;">{{section.mots|int|format_number}}</td>
<td style="text-align: right; padding: 10px; border-bottom: 1px solid #eee;">{{section.signes|int|format_number}}</td> <td style="text-align: right; padding: 10px; border-bottom: 1px solid #eee;">{{section.signes|int|format_number}}</td>
<td style="text-align: right; padding: 10px; border-bottom: 1px solid #eee;"> <td style="text-align: right; padding: 10px; border-bottom: 1px solid #eee;">
{% if section.manquants > 0 %} <span class="tooltip-container" tabindex="0" role="button" aria-label="Détails sur les mots manquants">
<span style="color: #f44336;">{{section.manquants|int|format_number}}</span> {% if section.manquants > 0 %}
{% else %} <span style="color: #f44336;">{{section.manquants|int|format_number}}</span>
<span style="color: #4CAF50;">✓</span> {% else %}
{% endif %} <span style="color: #4CAF50;">✓</span>
{% endif %}
<span class="tooltip-text">
<strong>Section: {{section.titre}}</strong><br>
<strong>Objectif (niveau {{section.niveau}}):</strong> {{section.objectif|int|format_number}} mots<br>
<strong>Réalisé:</strong> {{section.mots|int|format_number}} mots<br>
<strong>Signes:</strong> {{section.signes|int|format_number}}<br><br>
<strong>État:</strong><br>
{% if section.depasse %}
✓ Objectif atteint ! (+{{(section.mots - section.objectif)|int|format_number}} mots au-delà de l'objectif)
{% else %}
En cours ({{section.manquants|int|format_number}} mots manquants pour atteindre l'objectif)
{% endif %}
</span>
</span>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
@ -310,6 +375,28 @@
</table> </table>
{% endif %} {% endif %}
{% if domaines_liens %}
<h2>Domaines des sites web liés</h2>
<table style="width: 100%; border-collapse: collapse; margin-bottom: 30px;">
<thead>
<tr>
<th style="text-align: left; padding: 10px; border-bottom: 2px solid #ddd;">Rang</th>
<th style="text-align: left; padding: 10px; border-bottom: 2px solid #ddd;">Domaine</th>
<th style="text-align: right; padding: 10px; border-bottom: 2px solid #ddd;">Occurrences</th>
</tr>
</thead>
<tbody>
{% for domaine, nombre in domaines_liens %}
<tr>
<td style="padding: 10px; border-bottom: 1px solid #eee; color: #666;">{{loop.index}}</td>
<td style="padding: 10px; border-bottom: 1px solid #eee;"><code style="background-color: #f5f5f5; padding: 2px 6px; border-radius: 3px;">{{domaine}}</code></td>
<td style="text-align: right; padding: 10px; border-bottom: 1px solid #eee;"><strong>{{nombre|int|format_number}}</strong></td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
<h2>Statistiques détaillées par mois</h2> <h2>Statistiques détaillées par mois</h2>
<table> <table>
<thead> <thead>