mirror of
https://forge.chapril.org/tykayn/orgmode-to-gemini-blog
synced 2025-11-19 23:00:35 +01:00
génération de stats pour un seul fichier org
This commit is contained in:
parent
d404c6a7c8
commit
2e561080a9
4 changed files with 534 additions and 53 deletions
26
Makefile
26
Makefile
|
|
@ -18,6 +18,8 @@ help:
|
|||
@echo " make stats Génère les statistiques combinées pour tykayn_blog, cipherbliss_blog et qzine_blog"
|
||||
@echo " make stats-all Génère les statistiques pour tous les blogs"
|
||||
@echo " make stats-custom Génère les statistiques combinées personnalisées (modifiez la cible dans le Makefile)"
|
||||
@echo " make stats-file Génère les statistiques d'un fichier unique (exemple: sources/tykayn_blog/lang_fr/2024-01-article.org)"
|
||||
@echo " Utilisez: make stats-file FILE=chemin/vers/fichier.org [DATE_DEBUT=YYYY-MM-DD]"
|
||||
@echo ""
|
||||
@echo "Export EPUB:"
|
||||
@echo " make epub-tykayn Exporte tykayn_blog en EPUB"
|
||||
|
|
@ -27,13 +29,31 @@ help:
|
|||
@echo " make epub Exporte tous les blogs en EPUB (modifiez la cible dans le Makefile)"
|
||||
|
||||
stats:
|
||||
python3 generate_blog_stats.py tykayn_blog cipherbliss_blog qzine_blog --output tk_combinaison_stats.html
|
||||
python3 generate_blog_stats.py cipherbliss_blog
|
||||
|
||||
stats-all:
|
||||
python3 generate_blog_stats.py
|
||||
python3 generate_blog_stats.py tykayn_blog cipherbliss_blog qzine_blog --output tk_combinaison_stats.html
|
||||
|
||||
stats-custom:
|
||||
python3 generate_blog_stats.py tykayn_blog cipherbliss_blog qzine_blog --output tk_combinaison_stats.html --objectif-articles 10
|
||||
python3 generate_blog_stats.py tykayn_blog cipherbliss_blog qzine_blog --output tk_combinaison_stats.html --objectif-articles 3
|
||||
|
||||
stats-file:
|
||||
@if [ -z "$(FILE)" ]; then \
|
||||
echo "Erreur: Veuillez spécifier un fichier avec FILE=chemin/vers/fichier.org"; \
|
||||
echo ""; \
|
||||
echo "Exemples:"; \
|
||||
echo " make stats-file FILE=sources/tykayn_blog/lang_fr/2024-01-article.org"; \
|
||||
echo " make stats-file FILE=sources/qzine_blog/lang_fr/20120114T183335__fetichisme-des-poignees-de-porte-zetes-prevenus.org"; \
|
||||
echo ""; \
|
||||
echo "Avec date de début d'écriture:"; \
|
||||
echo " make stats-file FILE=sources/qzine_blog/lang_fr/20120114T183335__fetichisme-des-poignees-de-porte-zetes-prevenus.org DATE_DEBUT=2012-01-14"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@if [ -n "$(DATE_DEBUT)" ]; then \
|
||||
python3 generate_blog_stats.py "$(FILE)" --date-debut "$(DATE_DEBUT)"; \
|
||||
else \
|
||||
python3 generate_blog_stats.py "$(FILE)"; \
|
||||
fi
|
||||
|
||||
epub-tykayn:
|
||||
python3 export_to_epub.py tykayn_blog
|
||||
|
|
|
|||
|
|
@ -99,67 +99,120 @@ def extraire_date_du_fichier(filename):
|
|||
return None
|
||||
|
||||
|
||||
def compter_mots(contenu):
|
||||
def nettoyer_contenu(contenu, est_markdown=False):
|
||||
"""Nettoie le contenu en supprimant les métadonnées."""
|
||||
if est_markdown:
|
||||
# Supprimer le frontmatter YAML si présent
|
||||
contenu = re.sub(r'^---\n.*?\n---\n', '', contenu, flags=re.DOTALL | re.MULTILINE)
|
||||
return contenu.strip()
|
||||
else:
|
||||
# Utiliser find_extract_in_content_org pour nettoyer le contenu org
|
||||
return find_extract_in_content_org(contenu)
|
||||
|
||||
|
||||
def compter_mots(contenu, est_markdown=False):
|
||||
"""Compte le nombre de mots dans le contenu (sans les métadonnées)."""
|
||||
# Utiliser find_extract_in_content_org pour nettoyer le contenu
|
||||
contenu_clean = find_extract_in_content_org(contenu)
|
||||
# Nettoyer le contenu
|
||||
contenu_clean = nettoyer_contenu(contenu, est_markdown)
|
||||
|
||||
# Supprimer les liens org-mode pour ne compter que le texte
|
||||
contenu_clean = re.sub(r'\[\[([^\]]+)\]\[([^\]]+)\]\]', r'\2', contenu_clean)
|
||||
contenu_clean = re.sub(r'\[\[([^\]]+)\]\]', r'\1', contenu_clean)
|
||||
if est_markdown:
|
||||
# Supprimer les liens markdown [texte](url) pour ne compter que le texte
|
||||
contenu_clean = re.sub(r'\[([^\]]+)\]\([^\)]+\)', r'\1', contenu_clean)
|
||||
else:
|
||||
# Supprimer les liens org-mode pour ne compter que le texte
|
||||
contenu_clean = re.sub(r'\[\[([^\]]+)\]\[([^\]]+)\]\]', r'\2', contenu_clean)
|
||||
contenu_clean = re.sub(r'\[\[([^\]]+)\]\]', r'\1', contenu_clean)
|
||||
|
||||
# Compter les mots
|
||||
mots = contenu_clean.split()
|
||||
return len([m for m in mots if len(m.strip()) > 0])
|
||||
|
||||
|
||||
def compter_signes(contenu):
|
||||
def compter_signes(contenu, est_markdown=False):
|
||||
"""Compte le nombre de signes espaces compris dans le contenu."""
|
||||
# Utiliser find_extract_in_content_org pour nettoyer le contenu
|
||||
contenu_clean = find_extract_in_content_org(contenu)
|
||||
# Nettoyer le contenu
|
||||
contenu_clean = nettoyer_contenu(contenu, est_markdown)
|
||||
return len(contenu_clean)
|
||||
|
||||
|
||||
def compter_liens(contenu):
|
||||
def compter_liens(contenu, est_markdown=False):
|
||||
"""
|
||||
Compte le nombre de liens dans le contenu (format [[url]] ou [[url][texte]]).
|
||||
Compte le nombre de liens dans le contenu.
|
||||
Pour org: format [[url]] ou [[url][texte]]
|
||||
Pour markdown: format [texte](url) ou  pour images
|
||||
Distingue les liens vers des images des autres liens.
|
||||
Retourne un tuple (nb_liens_images, nb_liens_autres)
|
||||
"""
|
||||
# Compter les liens org-mode
|
||||
liens = re.findall(r'\[\[([^\]]+)\](\[[^\]]+\])?\]', contenu)
|
||||
|
||||
nb_images = 0
|
||||
nb_autres = 0
|
||||
|
||||
# Extensions d'images courantes
|
||||
extensions_images = ['.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp', '.bmp', '.ico']
|
||||
|
||||
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 est_markdown:
|
||||
# Compter les liens markdown: [texte](url) ou 
|
||||
liens_images = re.findall(r'!\[([^\]]*)\]\(([^\)]+)\)', contenu)
|
||||
liens_autres = re.findall(r'(?<!\!)\[([^\]]+)\]\(([^\)]+)\)', contenu)
|
||||
|
||||
if is_image:
|
||||
nb_images += 1
|
||||
else:
|
||||
nb_autres += 1
|
||||
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 [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
|
||||
|
||||
return nb_images, nb_autres
|
||||
|
||||
|
||||
def analyser_article(filepath):
|
||||
"""
|
||||
Analyse un fichier article et retourne ses statistiques.
|
||||
Analyse un fichier article (org ou markdown) et retourne ses statistiques.
|
||||
"""
|
||||
try:
|
||||
with open(filepath, 'r', encoding='utf-8') as f:
|
||||
contenu = f.read()
|
||||
|
||||
# Détecter le type de fichier
|
||||
ext = os.path.splitext(filepath)[1].lower()
|
||||
est_markdown = ext in ['.md', '.markdown']
|
||||
|
||||
# Extraire la date
|
||||
date_pub = extraire_date_du_contenu(contenu)
|
||||
date_pub = None
|
||||
if not est_markdown:
|
||||
date_pub = extraire_date_du_contenu(contenu)
|
||||
else:
|
||||
# Pour markdown, chercher dans le frontmatter YAML
|
||||
match = re.search(r'^---\n.*?date:\s*(.+?)\n', contenu, re.DOTALL | re.MULTILINE)
|
||||
if match:
|
||||
try:
|
||||
date_pub = datetime.strptime(match.group(1).strip(), '%Y-%m-%d')
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if not date_pub:
|
||||
date_pub = extraire_date_du_fichier(os.path.basename(filepath))
|
||||
if not date_pub:
|
||||
|
|
@ -167,9 +220,9 @@ def analyser_article(filepath):
|
|||
date_pub = datetime.fromtimestamp(os.path.getmtime(filepath))
|
||||
|
||||
# Calculer les statistiques
|
||||
nb_mots = compter_mots(contenu)
|
||||
nb_signes = compter_signes(contenu)
|
||||
nb_liens_images, nb_liens_autres = compter_liens(contenu)
|
||||
nb_mots = compter_mots(contenu, est_markdown)
|
||||
nb_signes = compter_signes(contenu, est_markdown)
|
||||
nb_liens_images, nb_liens_autres = compter_liens(contenu, est_markdown)
|
||||
|
||||
# Temps de lecture (en minutes)
|
||||
temps_lecture = nb_mots / LECTURE_MOTS_PAR_MINUTE if nb_mots > 0 else 0
|
||||
|
|
@ -338,6 +391,9 @@ def calculer_nanowrimo_mois(mois_cle, stats_mois, aujourdhui, objectif_quotidien
|
|||
else:
|
||||
signes_par_jour_moyen = 0
|
||||
|
||||
# Récupérer les articles du mois (triés par date)
|
||||
articles_du_mois = sorted(stats_mois.get('articles', []), key=lambda x: x['date'])
|
||||
|
||||
return {
|
||||
'mois_cle': mois_cle,
|
||||
'mois_formate': mois_date.strftime('%B %Y'),
|
||||
|
|
@ -354,7 +410,8 @@ def calculer_nanowrimo_mois(mois_cle, stats_mois, aujourdhui, objectif_quotidien
|
|||
'est_mois_futur': mois_date > aujourdhui,
|
||||
'objectif_quotidien': objectif_quotidien_utilise,
|
||||
'reste_a_faire': reste_a_faire,
|
||||
'signes_par_jour_moyen': signes_par_jour_moyen
|
||||
'signes_par_jour_moyen': signes_par_jour_moyen,
|
||||
'articles_du_mois': articles_du_mois
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -469,9 +526,23 @@ def generer_graphiques(blog_name, stats_par_mois, output_dir):
|
|||
ax.set_title(f'Articles publiés par mois - {blog_name}', fontsize=14, fontweight='bold')
|
||||
ax.legend(loc='upper left')
|
||||
ax.grid(True, alpha=0.3, axis='y')
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
|
||||
ax.xaxis.set_major_locator(mdates.MonthLocator(interval=max(1, len(mois)//12)))
|
||||
|
||||
# Améliorer le formatage des dates pour éviter la superposition
|
||||
nb_mois = len(mois)
|
||||
if nb_mois > 24:
|
||||
interval = max(3, nb_mois // 12)
|
||||
format_str = '%Y'
|
||||
elif nb_mois > 12:
|
||||
interval = max(2, nb_mois // 12)
|
||||
format_str = '%Y-%m'
|
||||
else:
|
||||
interval = 1
|
||||
format_str = '%Y-%m'
|
||||
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter(format_str))
|
||||
ax.xaxis.set_major_locator(mdates.MonthLocator(interval=interval))
|
||||
plt.xticks(rotation=45, ha='right')
|
||||
plt.setp(ax.xaxis.get_majorticklabels(), rotation=45, ha='right')
|
||||
plt.tight_layout()
|
||||
graph1_path = os.path.join(graph_dir, 'articles_par_mois.png')
|
||||
plt.savefig(graph1_path, dpi=150, bbox_inches='tight')
|
||||
|
|
@ -502,9 +573,23 @@ def generer_graphiques(blog_name, stats_par_mois, output_dir):
|
|||
ax.set_title(f'Mots totaux par mois - {blog_name}', fontsize=14, fontweight='bold')
|
||||
ax.legend(loc='upper left')
|
||||
ax.grid(True, alpha=0.3)
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
|
||||
ax.xaxis.set_major_locator(mdates.MonthLocator(interval=max(1, len(mois)//12)))
|
||||
|
||||
# Améliorer le formatage des dates pour éviter la superposition
|
||||
nb_mois = len(mois)
|
||||
if nb_mois > 24:
|
||||
interval = max(3, nb_mois // 12)
|
||||
format_str = '%Y'
|
||||
elif nb_mois > 12:
|
||||
interval = max(2, nb_mois // 12)
|
||||
format_str = '%Y-%m'
|
||||
else:
|
||||
interval = 1
|
||||
format_str = '%Y-%m'
|
||||
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter(format_str))
|
||||
ax.xaxis.set_major_locator(mdates.MonthLocator(interval=interval))
|
||||
plt.xticks(rotation=45, ha='right')
|
||||
plt.setp(ax.xaxis.get_majorticklabels(), rotation=45, ha='right')
|
||||
plt.tight_layout()
|
||||
graph2_path = os.path.join(graph_dir, 'mots_par_mois.png')
|
||||
plt.savefig(graph2_path, dpi=150, bbox_inches='tight')
|
||||
|
|
@ -518,9 +603,23 @@ def generer_graphiques(blog_name, stats_par_mois, output_dir):
|
|||
ax.set_ylabel('Temps de lecture (minutes)', fontsize=12)
|
||||
ax.set_title(f'Temps de lecture par mois - {blog_name}', fontsize=14, fontweight='bold')
|
||||
ax.grid(True, alpha=0.3, axis='y')
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
|
||||
ax.xaxis.set_major_locator(mdates.MonthLocator(interval=max(1, len(mois)//12)))
|
||||
|
||||
# Améliorer le formatage des dates pour éviter la superposition
|
||||
nb_mois = len(mois)
|
||||
if nb_mois > 24:
|
||||
interval = max(3, nb_mois // 12)
|
||||
format_str = '%Y'
|
||||
elif nb_mois > 12:
|
||||
interval = max(2, nb_mois // 12)
|
||||
format_str = '%Y-%m'
|
||||
else:
|
||||
interval = 1
|
||||
format_str = '%Y-%m'
|
||||
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter(format_str))
|
||||
ax.xaxis.set_major_locator(mdates.MonthLocator(interval=interval))
|
||||
plt.xticks(rotation=45, ha='right')
|
||||
plt.setp(ax.xaxis.get_majorticklabels(), rotation=45, ha='right')
|
||||
plt.tight_layout()
|
||||
graph3_path = os.path.join(graph_dir, 'temps_lecture_par_mois.png')
|
||||
plt.savefig(graph3_path, dpi=150, bbox_inches='tight')
|
||||
|
|
@ -804,9 +903,22 @@ def generer_graphiques_combines(blogs_data, output_dir):
|
|||
ax.set_title('Articles publiés par mois - Comparaison des blogs', fontsize=14, fontweight='bold')
|
||||
ax.legend(loc='upper left')
|
||||
ax.grid(True, alpha=0.3, axis='y')
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
|
||||
ax.xaxis.set_major_locator(mdates.MonthLocator(interval=max(1, len(mois_dates)//12)))
|
||||
plt.xticks([mdates.date2num(m) for m in mois_dates], rotation=45, ha='right')
|
||||
|
||||
# Améliorer le formatage des dates pour éviter la superposition
|
||||
nb_mois = len(mois_dates)
|
||||
if nb_mois > 24:
|
||||
interval = max(3, nb_mois // 12)
|
||||
format_str = '%Y'
|
||||
elif nb_mois > 12:
|
||||
interval = max(2, nb_mois // 12)
|
||||
format_str = '%Y-%m'
|
||||
else:
|
||||
interval = 1
|
||||
format_str = '%Y-%m'
|
||||
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter(format_str))
|
||||
ax.xaxis.set_major_locator(mdates.MonthLocator(interval=interval))
|
||||
plt.setp(ax.xaxis.get_majorticklabels(), rotation=45, ha='right')
|
||||
plt.tight_layout()
|
||||
|
||||
graph1_path = os.path.join(graph_dir, 'articles_par_mois_combines.png')
|
||||
|
|
@ -857,9 +969,22 @@ def generer_graphiques_combines(blogs_data, output_dir):
|
|||
ax.set_title('Mots publiés par mois - Comparaison des blogs', fontsize=14, fontweight='bold')
|
||||
ax.legend(loc='upper left')
|
||||
ax.grid(True, alpha=0.3, axis='y')
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
|
||||
ax.xaxis.set_major_locator(mdates.MonthLocator(interval=max(1, len(mois_dates)//12)))
|
||||
plt.xticks([mdates.date2num(m) for m in mois_dates], rotation=45, ha='right')
|
||||
|
||||
# Améliorer le formatage des dates pour éviter la superposition
|
||||
nb_mois = len(mois_dates)
|
||||
if nb_mois > 24:
|
||||
interval = max(3, nb_mois // 12)
|
||||
format_str = '%Y'
|
||||
elif nb_mois > 12:
|
||||
interval = max(2, nb_mois // 12)
|
||||
format_str = '%Y-%m'
|
||||
else:
|
||||
interval = 1
|
||||
format_str = '%Y-%m'
|
||||
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter(format_str))
|
||||
ax.xaxis.set_major_locator(mdates.MonthLocator(interval=interval))
|
||||
plt.setp(ax.xaxis.get_majorticklabels(), rotation=45, ha='right')
|
||||
plt.tight_layout()
|
||||
|
||||
graph2_path = os.path.join(graph_dir, 'mots_par_mois_combines.png')
|
||||
|
|
@ -910,9 +1035,22 @@ def generer_graphiques_combines(blogs_data, output_dir):
|
|||
ax.set_title('Signes publiés par mois - Comparaison des blogs', fontsize=14, fontweight='bold')
|
||||
ax.legend(loc='upper left')
|
||||
ax.grid(True, alpha=0.3, axis='y')
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
|
||||
ax.xaxis.set_major_locator(mdates.MonthLocator(interval=max(1, len(mois_dates)//12)))
|
||||
plt.xticks([mdates.date2num(m) for m in mois_dates], rotation=45, ha='right')
|
||||
|
||||
# Améliorer le formatage des dates pour éviter la superposition
|
||||
nb_mois = len(mois_dates)
|
||||
if nb_mois > 24:
|
||||
interval = max(3, nb_mois // 12)
|
||||
format_str = '%Y'
|
||||
elif nb_mois > 12:
|
||||
interval = max(2, nb_mois // 12)
|
||||
format_str = '%Y-%m'
|
||||
else:
|
||||
interval = 1
|
||||
format_str = '%Y-%m'
|
||||
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter(format_str))
|
||||
ax.xaxis.set_major_locator(mdates.MonthLocator(interval=interval))
|
||||
plt.setp(ax.xaxis.get_majorticklabels(), rotation=45, ha='right')
|
||||
plt.tight_layout()
|
||||
|
||||
graph3_path = os.path.join(graph_dir, 'signes_par_mois_combines.png')
|
||||
|
|
@ -1052,17 +1190,197 @@ def generer_page_combinee(blogs_data, output_file, html_websites_dir, env, objec
|
|||
print(f"\n✓ Page combinée générée: {html_path}")
|
||||
|
||||
|
||||
def generer_statistiques_fichier(filepath, html_websites_dir, env, template, date_debut_ecriture=None, objectif_quotidien=None, objectif_mensuel=None):
|
||||
"""
|
||||
Génère les statistiques pour un seul fichier org ou markdown.
|
||||
Si date_debut_ecriture est None, utilise le 1er du mois courant.
|
||||
"""
|
||||
import calendar
|
||||
|
||||
# Vérifier que le fichier existe
|
||||
if not os.path.exists(filepath):
|
||||
print(f"Erreur: Le fichier {filepath} n'existe pas")
|
||||
return None
|
||||
|
||||
# Vérifier l'extension
|
||||
ext = os.path.splitext(filepath)[1].lower()
|
||||
if ext not in ['.org', '.md', '.markdown']:
|
||||
print(f"Erreur: Le fichier doit être en .org, .md ou .markdown (reçu: {ext})")
|
||||
return None
|
||||
|
||||
print(f"Analyse du fichier {filepath}...")
|
||||
|
||||
# Analyser le fichier
|
||||
article = analyser_article(filepath)
|
||||
if not article:
|
||||
print(f"Impossible d'analyser le fichier {filepath}")
|
||||
return None
|
||||
|
||||
# Déterminer la date de début d'écriture
|
||||
aujourdhui = datetime.now()
|
||||
if date_debut_ecriture is None:
|
||||
# Par défaut : 1er du mois courant
|
||||
date_debut_ecriture = aujourdhui.replace(day=1)
|
||||
|
||||
# Calculer les statistiques par jour, mois, année depuis la date de début
|
||||
date_article = article['date']
|
||||
if date_article < date_debut_ecriture:
|
||||
date_debut_ecriture = date_article # Ajuster si l'article est plus ancien
|
||||
|
||||
# Calculer le nombre de jours/mois/années depuis le début
|
||||
delta = aujourdhui - date_debut_ecriture
|
||||
nb_jours_ecriture = max(1, delta.days + 1) # Au moins 1 jour
|
||||
|
||||
# Calculer les moyennes par jour, mois, année
|
||||
articles_par_jour_moyen = 1 / nb_jours_ecriture
|
||||
articles_par_mois_moyen = 1 / max(1, nb_jours_ecriture / 30)
|
||||
articles_par_annee_moyen = 1 / max(1, nb_jours_ecriture / 365)
|
||||
|
||||
mots_par_jour_moyen = article['mots'] / nb_jours_ecriture
|
||||
mots_par_mois_moyen = article['mots'] / max(1, nb_jours_ecriture / 30)
|
||||
mots_par_annee_moyen = article['mots'] / max(1, nb_jours_ecriture / 365)
|
||||
|
||||
signes_par_jour_moyen = article['signes'] / nb_jours_ecriture
|
||||
signes_par_mois_moyen = article['signes'] / max(1, nb_jours_ecriture / 30)
|
||||
signes_par_annee_moyen = article['signes'] / max(1, nb_jours_ecriture / 365)
|
||||
|
||||
temps_lecture_par_jour_moyen = article['temps_lecture'] / nb_jours_ecriture
|
||||
temps_lecture_par_mois_moyen = article['temps_lecture'] / max(1, nb_jours_ecriture / 30)
|
||||
temps_lecture_par_annee_moyen = article['temps_lecture'] / max(1, nb_jours_ecriture / 365)
|
||||
|
||||
liens_par_jour_moyen = article['liens'] / nb_jours_ecriture
|
||||
liens_par_mois_moyen = article['liens'] / max(1, nb_jours_ecriture / 30)
|
||||
liens_par_annee_moyen = article['liens'] / max(1, nb_jours_ecriture / 365)
|
||||
|
||||
# Créer une structure de données compatible avec les templates
|
||||
articles = [article]
|
||||
stats_par_mois = calculer_statistiques_par_mois(articles)
|
||||
|
||||
# Calculer les stats NaNoWriMo pour le mois courant (depuis date_debut_ecriture)
|
||||
mois_courant_cle = date_debut_ecriture.strftime('%Y-%m')
|
||||
stats_mois = stats_par_mois.get(mois_courant_cle, {
|
||||
'articles': articles,
|
||||
'signes_total': article['signes'],
|
||||
'nb_articles': 1,
|
||||
'mots_total': article['mots'],
|
||||
'liens_total': article['liens'],
|
||||
'liens_images_total': article['liens_images'],
|
||||
'liens_autres_total': article['liens_autres'],
|
||||
'temps_lecture_total': article['temps_lecture']
|
||||
})
|
||||
|
||||
# Calculer NaNoWriMo en considérant que l'écriture a commencé à date_debut_ecriture
|
||||
stats_nanowrimo_mois = calculer_nanowrimo_mois(mois_courant_cle, stats_mois, aujourdhui, objectif_quotidien, objectif_mensuel)
|
||||
# Ajuster jour_actuel pour refléter les jours depuis date_debut_ecriture
|
||||
jours_ecoules = (aujourdhui - date_debut_ecriture).days + 1
|
||||
stats_nanowrimo_mois['jour_actuel'] = min(jours_ecoules, stats_nanowrimo_mois['jours_dans_mois'])
|
||||
# Recalculer objectif_jusqu_aujourdhui
|
||||
stats_nanowrimo_mois['objectif_jusqu_aujourdhui'] = stats_nanowrimo_mois['objectif_quotidien'] * jours_ecoules
|
||||
# Recalculer signes_par_jour_moyen
|
||||
if jours_ecoules > 0:
|
||||
stats_nanowrimo_mois['signes_par_jour_moyen'] = article['signes'] / jours_ecoules
|
||||
else:
|
||||
stats_nanowrimo_mois['signes_par_jour_moyen'] = 0
|
||||
# Recalculer reste_a_faire
|
||||
stats_nanowrimo_mois['reste_a_faire'] = max(0, stats_nanowrimo_mois['objectif_jusqu_aujourdhui'] - article['signes'])
|
||||
# Ajouter les propriétés manquantes pour compatibilité avec le template
|
||||
stats_nanowrimo_mois['objectif_articles'] = None # Pas d'objectif d'articles pour un fichier unique
|
||||
stats_nanowrimo_mois['articles_realises'] = 1 # Un seul article (le fichier)
|
||||
|
||||
stats_nanowrimo = [stats_nanowrimo_mois]
|
||||
|
||||
# Créer le dossier de sortie
|
||||
filename_base = os.path.splitext(os.path.basename(filepath))[0]
|
||||
output_dir = os.path.join(html_websites_dir, 'fichiers_analyses')
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
# Copier le fichier CSS
|
||||
css_source = 'templates/styles/stats.css'
|
||||
css_dest = os.path.join(output_dir, 'stats.css')
|
||||
if os.path.exists(css_source):
|
||||
shutil.copy2(css_source, css_dest)
|
||||
|
||||
# Calculer les statistiques globales
|
||||
nb_articles_total = 1
|
||||
mots_total = article['mots']
|
||||
signes_total = article['signes']
|
||||
liens_total = article['liens']
|
||||
liens_images_total = article['liens_images']
|
||||
liens_autres_total = article['liens_autres']
|
||||
temps_lecture_total = article['temps_lecture']
|
||||
|
||||
# Générer le HTML
|
||||
date_gen = datetime.now().strftime('%d/%m/%Y à %H:%M:%S')
|
||||
date_article_str = date_article.strftime('%d/%m/%Y') if date_article else 'N/A'
|
||||
|
||||
html_content = template.render(
|
||||
blog_title=f"Analyse: {os.path.basename(filepath)}",
|
||||
author='',
|
||||
nb_articles_total=nb_articles_total,
|
||||
mots_total=mots_total,
|
||||
signes_total=signes_total,
|
||||
liens_total=liens_total,
|
||||
liens_images_total=liens_images_total,
|
||||
liens_autres_total=liens_autres_total,
|
||||
temps_lecture_total=temps_lecture_total,
|
||||
mots_moyen=mots_total,
|
||||
liens_moyen=liens_total,
|
||||
liens_images_moyen=liens_images_total,
|
||||
liens_autres_moyen=liens_autres_total,
|
||||
signes_moyen=signes_total,
|
||||
temps_lecture_par_article=temps_lecture_total,
|
||||
frequence=0,
|
||||
premiere_date_str=date_article_str,
|
||||
derniere_date_str=date_article_str,
|
||||
stats_par_mois=stats_par_mois,
|
||||
stats_nanowrimo=stats_nanowrimo,
|
||||
graphiques=[],
|
||||
date_gen=date_gen,
|
||||
lecture_mots_par_minute=LECTURE_MOTS_PAR_MINUTE,
|
||||
css_path='stats.css',
|
||||
fichier_source=filepath,
|
||||
date_article=date_article_str,
|
||||
date_debut_ecriture=date_debut_ecriture.strftime('%d/%m/%Y'),
|
||||
articles_par_jour_moyen=articles_par_jour_moyen,
|
||||
articles_par_mois_moyen=articles_par_mois_moyen,
|
||||
articles_par_annee_moyen=articles_par_annee_moyen,
|
||||
mots_par_jour_moyen=mots_par_jour_moyen,
|
||||
mots_par_mois_moyen=mots_par_mois_moyen,
|
||||
mots_par_annee_moyen=mots_par_annee_moyen,
|
||||
signes_par_jour_moyen=signes_par_jour_moyen,
|
||||
signes_par_mois_moyen=signes_par_mois_moyen,
|
||||
signes_par_annee_moyen=signes_par_annee_moyen,
|
||||
temps_lecture_par_jour_moyen=temps_lecture_par_jour_moyen,
|
||||
temps_lecture_par_mois_moyen=temps_lecture_par_mois_moyen,
|
||||
temps_lecture_par_annee_moyen=temps_lecture_par_annee_moyen,
|
||||
liens_par_jour_moyen=liens_par_jour_moyen,
|
||||
liens_par_mois_moyen=liens_par_mois_moyen,
|
||||
liens_par_annee_moyen=liens_par_annee_moyen,
|
||||
nb_jours_avec_articles=1,
|
||||
nb_mois_avec_articles=1,
|
||||
nb_annees_avec_articles=1
|
||||
)
|
||||
|
||||
# Sauvegarder le HTML
|
||||
html_path = os.path.join(output_dir, f'{filename_base}_stats.html')
|
||||
with open(html_path, 'w', encoding='utf-8') as f:
|
||||
f.write(html_content)
|
||||
|
||||
print(f" ✓ Statistiques générées: {html_path}")
|
||||
return html_path
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Fonction principale qui analyse les blogs et génère les pages de statistiques.
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Génère des statistiques détaillées sur les blogs'
|
||||
description='Génère des statistiques détaillées sur les blogs ou un fichier unique'
|
||||
)
|
||||
parser.add_argument(
|
||||
'blogs',
|
||||
nargs='*',
|
||||
help='Noms des blogs à analyser (si vide, analyse tous les blogs)'
|
||||
help='Noms des blogs à analyser ou chemin vers un fichier .org/.md/.markdown (si vide, analyse tous les blogs)'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--output',
|
||||
|
|
@ -1084,6 +1402,11 @@ def main():
|
|||
type=int,
|
||||
help='Objectif de signes par mois (prend le pas sur l\'objectif quotidien et NaNoWriMo)'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--date-debut',
|
||||
type=str,
|
||||
help='Date de début d\'écriture au format YYYY-MM-DD (pour l\'analyse d\'un fichier unique, défaut: 1er du mois courant)'
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
|
@ -1099,6 +1422,29 @@ def main():
|
|||
|
||||
template = env.get_template('templates/html/stats.html.j2')
|
||||
|
||||
# Vérifier si un seul fichier est fourni
|
||||
if args.blogs and len(args.blogs) == 1:
|
||||
filepath = args.blogs[0]
|
||||
# Vérifier si c'est un fichier (et pas un dossier de blog)
|
||||
if os.path.isfile(filepath):
|
||||
# Parser la date de début si fournie
|
||||
date_debut_ecriture = None
|
||||
if args.date_debut:
|
||||
try:
|
||||
date_debut_ecriture = datetime.strptime(args.date_debut, '%Y-%m-%d')
|
||||
except ValueError:
|
||||
print(f"Erreur: Format de date invalide pour --date-debut. Utilisez YYYY-MM-DD (ex: 2024-01-15)")
|
||||
return
|
||||
|
||||
# Récupérer les objectifs de signes
|
||||
objectif_quotidien = args.objectif_signes_quotidien
|
||||
objectif_mensuel = args.objectif_signes_mensuel
|
||||
|
||||
generer_statistiques_fichier(filepath, html_websites_dir, env, template,
|
||||
date_debut_ecriture, objectif_quotidien, objectif_mensuel)
|
||||
print("Terminé!")
|
||||
return
|
||||
|
||||
# Lister tous les dossiers de blogs si aucun n'est spécifié
|
||||
if not os.path.exists(sources_dir):
|
||||
print(f"Erreur: Le dossier {sources_dir} n'existe pas")
|
||||
|
|
|
|||
|
|
@ -14,26 +14,66 @@
|
|||
{% endif %}
|
||||
|
||||
<h2>Vue d'ensemble</h2>
|
||||
{% if date_debut_ecriture %}
|
||||
<p style="color: #666; margin-bottom: 15px;">
|
||||
<strong>Date de début d'écriture:</strong> {{date_debut_ecriture}}
|
||||
</p>
|
||||
{% endif %}
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<h3>Articles publiés</h3>
|
||||
<div class="value">{{nb_articles_total|int|format_number}}</div>
|
||||
{% if articles_par_jour_moyen is defined %}
|
||||
<div style="font-size: 0.9em; color: #666; margin-top: 5px;">
|
||||
Par jour: {{articles_par_jour_moyen|round(3)}} |
|
||||
Par mois: {{articles_par_mois_moyen|round(2)}} |
|
||||
Par année: {{articles_par_annee_moyen|round(2)}}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="stat-card blue">
|
||||
<h3>Mots totaux</h3>
|
||||
<div class="value">{{mots_total|int|format_number}}</div>
|
||||
{% if mots_par_jour_moyen is defined %}
|
||||
<div style="font-size: 0.9em; color: #666; margin-top: 5px;">
|
||||
Par jour: {{mots_par_jour_moyen|int|format_number}} |
|
||||
Par mois: {{mots_par_mois_moyen|int|format_number}} |
|
||||
Par année: {{mots_par_annee_moyen|int|format_number}}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="stat-card green">
|
||||
<h3>Signes (espaces inclus)</h3>
|
||||
<div class="value">{{signes_total|int|format_number}}</div>
|
||||
{% if signes_par_jour_moyen is defined %}
|
||||
<div style="font-size: 0.9em; color: #666; margin-top: 5px;">
|
||||
Par jour: {{signes_par_jour_moyen|int|format_number}} |
|
||||
Par mois: {{signes_par_mois_moyen|int|format_number}} |
|
||||
Par année: {{signes_par_annee_moyen|int|format_number}}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="stat-card orange">
|
||||
<h3>Temps de lecture total</h3>
|
||||
<div class="value">{{temps_lecture_total|format_duree}}</div>
|
||||
{% if temps_lecture_par_jour_moyen is defined %}
|
||||
<div style="font-size: 0.9em; color: #666; margin-top: 5px;">
|
||||
Par jour: {{temps_lecture_par_jour_moyen|format_duree}} |
|
||||
Par mois: {{temps_lecture_par_mois_moyen|format_duree}} |
|
||||
Par année: {{temps_lecture_par_annee_moyen|format_duree}}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="stat-card purple">
|
||||
<h3>Liens totaux</h3>
|
||||
<div class="value">{{liens_total|int|format_number}}</div>
|
||||
{% if liens_par_jour_moyen is defined %}
|
||||
<div style="font-size: 0.9em; color: #666; margin-top: 5px;">
|
||||
Par jour: {{liens_par_jour_moyen|round(2)}} |
|
||||
Par mois: {{liens_par_mois_moyen|round(1)}} |
|
||||
Par année: {{liens_par_annee_moyen|round(1)}}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -115,7 +155,12 @@
|
|||
<p><strong>Objectif quotidien:</strong> 1667 signes (espaces compris) par jour</p>
|
||||
{% for mois_stats in stats_nanowrimo %}
|
||||
<div class="nanowrimo-month">
|
||||
<h3>{{mois_stats.mois_formate}}</h3>
|
||||
<h3>
|
||||
{{mois_stats.mois_formate}}
|
||||
{% if mois_stats.depassement > 0 %}
|
||||
⭐ <span style="color: #FFC107; font-size: 0.9em;">+{{mois_stats.depassement|int|format_number}} signes</span>
|
||||
{% endif %}
|
||||
</h3>
|
||||
|
||||
{% if mois_stats.objectif_articles is not none %}
|
||||
<div class="progress-info">
|
||||
|
|
@ -198,6 +243,26 @@
|
|||
(objectif jusqu'à aujourd'hui: {{mois_stats.objectif_jusqu_aujourdhui|int|format_number}} signes)
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if mois_stats.articles_du_mois %}
|
||||
<div style="margin-top: 15px;">
|
||||
<strong>Articles du mois ({{mois_stats.articles_du_mois|length}}):</strong>
|
||||
<ul style="margin-top: 5px; padding-left: 20px;">
|
||||
{% for article in mois_stats.articles_du_mois %}
|
||||
<li>
|
||||
<strong>{{article.date.strftime('%d/%m/%Y')}}</strong> - {{article.fichier}}
|
||||
<span style="color: #666; font-size: 0.9em;">
|
||||
({{article.mots|int|format_number}} mots, {{article.signes|int|format_number}} signes)
|
||||
</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% else %}
|
||||
<div style="margin-top: 15px; color: #999; font-style: italic;">
|
||||
Aucun article ce mois
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -118,7 +118,12 @@
|
|||
{% endif %}
|
||||
{% for mois_stats in stats_nanowrimo_combines %}
|
||||
<div class="nanowrimo-month">
|
||||
<h3>{{mois_stats.mois_formate}}</h3>
|
||||
<h3>
|
||||
{{mois_stats.mois_formate}}
|
||||
{% if mois_stats.depassement > 0 %}
|
||||
⭐ <span style="color: #FFC107; font-size: 0.9em;">+{{mois_stats.depassement|int|format_number}} signes</span>
|
||||
{% endif %}
|
||||
</h3>
|
||||
|
||||
<div class="progress-info">
|
||||
<strong>Signes réalisés (tous blogs):</strong> {{mois_stats.signes_realises|int|format_number}}
|
||||
|
|
@ -192,6 +197,26 @@
|
|||
(objectif jusqu'à aujourd'hui: {{mois_stats.objectif_jusqu_aujourdhui|int|format_number}} signes)
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if mois_stats.articles_du_mois %}
|
||||
<div style="margin-top: 15px;">
|
||||
<strong>Articles du mois (tous blogs, {{mois_stats.articles_du_mois|length}}):</strong>
|
||||
<ul style="margin-top: 5px; padding-left: 20px;">
|
||||
{% for article in mois_stats.articles_du_mois %}
|
||||
<li>
|
||||
<strong>{{article.date.strftime('%d/%m/%Y')}}</strong> - {{article.fichier}}
|
||||
<span style="color: #666; font-size: 0.9em;">
|
||||
({{article.mots|int|format_number}} mots, {{article.signes|int|format_number}} signes)
|
||||
</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% else %}
|
||||
<div style="margin-top: 15px; color: #999; font-style: italic;">
|
||||
Aucun article ce mois
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
|
@ -286,7 +311,12 @@
|
|||
<p><strong>Objectif quotidien:</strong> 1667 signes (espaces compris) par jour</p>
|
||||
{% for mois_stats in blog_data.stats_nanowrimo %}
|
||||
<div class="nanowrimo-month">
|
||||
<h3>{{mois_stats.mois_formate}}</h3>
|
||||
<h3>
|
||||
{{mois_stats.mois_formate}}
|
||||
{% if mois_stats.depassement > 0 %}
|
||||
⭐ <span style="color: #FFC107; font-size: 0.9em;">+{{mois_stats.depassement|int|format_number}} signes</span>
|
||||
{% endif %}
|
||||
</h3>
|
||||
|
||||
{% if mois_stats.objectif_articles is not none %}
|
||||
<div class="progress-info">
|
||||
|
|
@ -369,6 +399,26 @@
|
|||
(objectif jusqu'à aujourd'hui: {{mois_stats.objectif_jusqu_aujourdhui|int|format_number}} signes)
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if mois_stats.articles_du_mois %}
|
||||
<div style="margin-top: 15px;">
|
||||
<strong>Articles du mois ({{mois_stats.articles_du_mois|length}}):</strong>
|
||||
<ul style="margin-top: 5px; padding-left: 20px;">
|
||||
{% for article in mois_stats.articles_du_mois %}
|
||||
<li>
|
||||
<strong>{{article.date.strftime('%d/%m/%Y')}}</strong> - {{article.fichier}}
|
||||
<span style="color: #666; font-size: 0.9em;">
|
||||
({{article.mots|int|format_number}} mots, {{article.signes|int|format_number}} signes)
|
||||
</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% else %}
|
||||
<div style="margin-top: 15px; color: #999; font-style: italic;">
|
||||
Aucun article ce mois
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue