mirror of
https://forge.chapril.org/tykayn/orgmode-to-gemini-blog
synced 2025-11-19 23:00:35 +01:00
up stats sections
This commit is contained in:
parent
c81aeb466c
commit
0ff2c68eac
3 changed files with 223 additions and 63 deletions
|
|
@ -10,7 +10,8 @@ import re
|
|||
import argparse
|
||||
import shutil
|
||||
from datetime import datetime
|
||||
from collections import defaultdict
|
||||
from collections import defaultdict, Counter
|
||||
from urllib.parse import urlparse
|
||||
import locale
|
||||
|
||||
try:
|
||||
|
|
@ -164,6 +165,62 @@ def compter_signes(contenu, est_markdown=False):
|
|||
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 
|
||||
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):
|
||||
"""
|
||||
Compte le nombre de liens dans le contenu.
|
||||
|
|
@ -178,45 +235,43 @@ def compter_liens(contenu, est_markdown=False):
|
|||
# Extensions d'images courantes
|
||||
extensions_images = ['.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp', '.bmp', '.ico']
|
||||
|
||||
if est_markdown:
|
||||
# Compter les liens markdown: [texte](url) ou 
|
||||
liens_images = re.findall(r'!\[([^\]]*)\]\(([^\)]+)\)', contenu)
|
||||
liens_autres = re.findall(r'(?<!\!)\[([^\]]+)\]\(([^\)]+)\)', contenu)
|
||||
urls = extraire_liens(contenu, est_markdown)
|
||||
|
||||
for url in [l[1] for l in liens_images]:
|
||||
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
|
||||
for url in urls:
|
||||
# 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()
|
||||
|
||||
for url in [l[1] for l in liens_autres]:
|
||||
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
|
||||
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
|
||||
if is_image:
|
||||
nb_images += 1
|
||||
else:
|
||||
nb_autres += 1
|
||||
|
||||
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):
|
||||
"""
|
||||
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_autres': nb_liens_autres,
|
||||
'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:
|
||||
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_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
|
||||
|
||||
# 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
|
||||
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,
|
||||
lecture_mots_par_minute=LECTURE_MOTS_PAR_MINUTE,
|
||||
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
|
||||
|
|
@ -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
|
||||
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
|
||||
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']
|
||||
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
|
||||
sections_stats = None
|
||||
if ext == '.org':
|
||||
|
|
@ -1509,7 +1579,8 @@ def generer_statistiques_fichier(filepath, html_websites_dir, env, template, dat
|
|||
nb_jours_avec_articles=1,
|
||||
nb_mois_avec_articles=1,
|
||||
nb_annees_avec_articles=1,
|
||||
sections_stats=sections_stats
|
||||
sections_stats=sections_stats,
|
||||
domaines_liens=domaines_liens
|
||||
)
|
||||
|
||||
# Sauvegarder le HTML
|
||||
|
|
|
|||
|
|
@ -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
|
||||
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
|
||||
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é
|
||||
[[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
|
||||
|
|
@ -49,7 +51,10 @@ branleur êtes vous ?]] et
|
|||
sextoys]] (avec des échantillons de personnes non sélectionnés)
|
||||
indiquent pour les personnes ayant répondu, que la masturbation fait
|
||||
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
|
||||
discussions entre membres de la famille.Il reste
|
||||
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),
|
||||
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
|
||||
préférence? En fait si.[caption id=""
|
||||
préférence? En fait si.
|
||||
|
||||
[caption id=""
|
||||
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]]
|
||||
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
|
||||
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
|
||||
|
|
@ -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.
|
||||
|
||||
|
||||
<<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
|
||||
2004à 22h15, dans le cadre d'une THEMA : Le
|
||||
sexedes
|
||||
femmes [[https://qzine.fr/wp-content/uploads/2016/05/mamiepointG.png]](mamie
|
||||
pantoufle par Milk)Heureusement que mamie pantoufle
|
||||
sexe des
|
||||
femmes
|
||||
|
||||
|
||||
[[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é!
|
||||
|
|
|
|||
|
|
@ -270,6 +270,57 @@
|
|||
|
||||
{% if sections_stats %}
|
||||
<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;">
|
||||
<thead>
|
||||
<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.signes|int|format_number}}</td>
|
||||
<td style="text-align: right; padding: 10px; border-bottom: 1px solid #eee;">
|
||||
{% if section.manquants > 0 %}
|
||||
<span style="color: #f44336;">{{section.manquants|int|format_number}}</span>
|
||||
{% else %}
|
||||
<span style="color: #4CAF50;">✓</span>
|
||||
{% endif %}
|
||||
<span class="tooltip-container" tabindex="0" role="button" aria-label="Détails sur les mots manquants">
|
||||
{% if section.manquants > 0 %}
|
||||
<span style="color: #f44336;">{{section.manquants|int|format_number}}</span>
|
||||
{% else %}
|
||||
<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>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
@ -310,6 +375,28 @@
|
|||
</table>
|
||||
{% 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>
|
||||
<table>
|
||||
<thead>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue