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 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-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-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 ""
|
||||||
@echo "Export EPUB:"
|
@echo "Export EPUB:"
|
||||||
@echo " make epub-tykayn Exporte tykayn_blog en 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)"
|
@echo " make epub Exporte tous les blogs en EPUB (modifiez la cible dans le Makefile)"
|
||||||
|
|
||||||
stats:
|
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:
|
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:
|
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:
|
epub-tykayn:
|
||||||
python3 export_to_epub.py tykayn_blog
|
python3 export_to_epub.py tykayn_blog
|
||||||
|
|
|
||||||
|
|
@ -99,67 +99,120 @@ def extraire_date_du_fichier(filename):
|
||||||
return None
|
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)."""
|
"""Compte le nombre de mots dans le contenu (sans les métadonnées)."""
|
||||||
# Utiliser find_extract_in_content_org pour nettoyer le contenu
|
# Nettoyer le contenu
|
||||||
contenu_clean = find_extract_in_content_org(contenu)
|
contenu_clean = nettoyer_contenu(contenu, est_markdown)
|
||||||
|
|
||||||
# Supprimer les liens org-mode pour ne compter que le texte
|
if est_markdown:
|
||||||
contenu_clean = re.sub(r'\[\[([^\]]+)\]\[([^\]]+)\]\]', r'\2', contenu_clean)
|
# Supprimer les liens markdown [texte](url) pour ne compter que le texte
|
||||||
contenu_clean = re.sub(r'\[\[([^\]]+)\]\]', r'\1', contenu_clean)
|
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
|
# Compter les mots
|
||||||
mots = contenu_clean.split()
|
mots = contenu_clean.split()
|
||||||
return len([m for m in mots if len(m.strip()) > 0])
|
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."""
|
"""Compte le nombre de signes espaces compris dans le contenu."""
|
||||||
# Utiliser find_extract_in_content_org pour nettoyer le contenu
|
# Nettoyer le contenu
|
||||||
contenu_clean = find_extract_in_content_org(contenu)
|
contenu_clean = nettoyer_contenu(contenu, est_markdown)
|
||||||
return len(contenu_clean)
|
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.
|
Distingue les liens vers des images des autres liens.
|
||||||
Retourne un tuple (nb_liens_images, nb_liens_autres)
|
Retourne un tuple (nb_liens_images, nb_liens_autres)
|
||||||
"""
|
"""
|
||||||
# Compter les liens org-mode
|
|
||||||
liens = re.findall(r'\[\[([^\]]+)\](\[[^\]]+\])?\]', contenu)
|
|
||||||
|
|
||||||
nb_images = 0
|
nb_images = 0
|
||||||
nb_autres = 0
|
nb_autres = 0
|
||||||
|
|
||||||
# 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']
|
||||||
|
|
||||||
for lien_match in liens:
|
if est_markdown:
|
||||||
url = lien_match[0]
|
# Compter les liens markdown: [texte](url) ou 
|
||||||
# Vérifier si c'est une image
|
liens_images = re.findall(r'!\[([^\]]*)\]\(([^\)]+)\)', contenu)
|
||||||
is_image = any(url.lower().endswith(ext) for ext in extensions_images) or \
|
liens_autres = re.findall(r'(?<!\!)\[([^\]]+)\]\(([^\)]+)\)', contenu)
|
||||||
'/image' in url.lower() or \
|
|
||||||
'img' in url.lower()
|
|
||||||
|
|
||||||
if is_image:
|
for url in [l[1] for l in liens_images]:
|
||||||
nb_images += 1
|
is_image = any(url.lower().endswith(ext) for ext in extensions_images) or \
|
||||||
else:
|
'/image' in url.lower() or \
|
||||||
nb_autres += 1
|
'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
|
return nb_images, nb_autres
|
||||||
|
|
||||||
|
|
||||||
def analyser_article(filepath):
|
def analyser_article(filepath):
|
||||||
"""
|
"""
|
||||||
Analyse un fichier article et retourne ses statistiques.
|
Analyse un fichier article (org ou markdown) et retourne ses statistiques.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
with open(filepath, 'r', encoding='utf-8') as f:
|
with open(filepath, 'r', encoding='utf-8') as f:
|
||||||
contenu = f.read()
|
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
|
# 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:
|
if not date_pub:
|
||||||
date_pub = extraire_date_du_fichier(os.path.basename(filepath))
|
date_pub = extraire_date_du_fichier(os.path.basename(filepath))
|
||||||
if not date_pub:
|
if not date_pub:
|
||||||
|
|
@ -167,9 +220,9 @@ def analyser_article(filepath):
|
||||||
date_pub = datetime.fromtimestamp(os.path.getmtime(filepath))
|
date_pub = datetime.fromtimestamp(os.path.getmtime(filepath))
|
||||||
|
|
||||||
# Calculer les statistiques
|
# Calculer les statistiques
|
||||||
nb_mots = compter_mots(contenu)
|
nb_mots = compter_mots(contenu, est_markdown)
|
||||||
nb_signes = compter_signes(contenu)
|
nb_signes = compter_signes(contenu, est_markdown)
|
||||||
nb_liens_images, nb_liens_autres = compter_liens(contenu)
|
nb_liens_images, nb_liens_autres = compter_liens(contenu, est_markdown)
|
||||||
|
|
||||||
# Temps de lecture (en minutes)
|
# Temps de lecture (en minutes)
|
||||||
temps_lecture = nb_mots / LECTURE_MOTS_PAR_MINUTE if nb_mots > 0 else 0
|
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:
|
else:
|
||||||
signes_par_jour_moyen = 0
|
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 {
|
return {
|
||||||
'mois_cle': mois_cle,
|
'mois_cle': mois_cle,
|
||||||
'mois_formate': mois_date.strftime('%B %Y'),
|
'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,
|
'est_mois_futur': mois_date > aujourdhui,
|
||||||
'objectif_quotidien': objectif_quotidien_utilise,
|
'objectif_quotidien': objectif_quotidien_utilise,
|
||||||
'reste_a_faire': reste_a_faire,
|
'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.set_title(f'Articles publiés par mois - {blog_name}', fontsize=14, fontweight='bold')
|
||||||
ax.legend(loc='upper left')
|
ax.legend(loc='upper left')
|
||||||
ax.grid(True, alpha=0.3, axis='y')
|
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.xticks(rotation=45, ha='right')
|
||||||
|
plt.setp(ax.xaxis.get_majorticklabels(), rotation=45, ha='right')
|
||||||
plt.tight_layout()
|
plt.tight_layout()
|
||||||
graph1_path = os.path.join(graph_dir, 'articles_par_mois.png')
|
graph1_path = os.path.join(graph_dir, 'articles_par_mois.png')
|
||||||
plt.savefig(graph1_path, dpi=150, bbox_inches='tight')
|
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.set_title(f'Mots totaux par mois - {blog_name}', fontsize=14, fontweight='bold')
|
||||||
ax.legend(loc='upper left')
|
ax.legend(loc='upper left')
|
||||||
ax.grid(True, alpha=0.3)
|
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.xticks(rotation=45, ha='right')
|
||||||
|
plt.setp(ax.xaxis.get_majorticklabels(), rotation=45, ha='right')
|
||||||
plt.tight_layout()
|
plt.tight_layout()
|
||||||
graph2_path = os.path.join(graph_dir, 'mots_par_mois.png')
|
graph2_path = os.path.join(graph_dir, 'mots_par_mois.png')
|
||||||
plt.savefig(graph2_path, dpi=150, bbox_inches='tight')
|
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_ylabel('Temps de lecture (minutes)', fontsize=12)
|
||||||
ax.set_title(f'Temps de lecture par mois - {blog_name}', fontsize=14, fontweight='bold')
|
ax.set_title(f'Temps de lecture par mois - {blog_name}', fontsize=14, fontweight='bold')
|
||||||
ax.grid(True, alpha=0.3, axis='y')
|
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.xticks(rotation=45, ha='right')
|
||||||
|
plt.setp(ax.xaxis.get_majorticklabels(), rotation=45, ha='right')
|
||||||
plt.tight_layout()
|
plt.tight_layout()
|
||||||
graph3_path = os.path.join(graph_dir, 'temps_lecture_par_mois.png')
|
graph3_path = os.path.join(graph_dir, 'temps_lecture_par_mois.png')
|
||||||
plt.savefig(graph3_path, dpi=150, bbox_inches='tight')
|
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.set_title('Articles publiés par mois - Comparaison des blogs', fontsize=14, fontweight='bold')
|
||||||
ax.legend(loc='upper left')
|
ax.legend(loc='upper left')
|
||||||
ax.grid(True, alpha=0.3, axis='y')
|
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)))
|
# Améliorer le formatage des dates pour éviter la superposition
|
||||||
plt.xticks([mdates.date2num(m) for m in mois_dates], rotation=45, ha='right')
|
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()
|
plt.tight_layout()
|
||||||
|
|
||||||
graph1_path = os.path.join(graph_dir, 'articles_par_mois_combines.png')
|
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.set_title('Mots publiés par mois - Comparaison des blogs', fontsize=14, fontweight='bold')
|
||||||
ax.legend(loc='upper left')
|
ax.legend(loc='upper left')
|
||||||
ax.grid(True, alpha=0.3, axis='y')
|
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)))
|
# Améliorer le formatage des dates pour éviter la superposition
|
||||||
plt.xticks([mdates.date2num(m) for m in mois_dates], rotation=45, ha='right')
|
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()
|
plt.tight_layout()
|
||||||
|
|
||||||
graph2_path = os.path.join(graph_dir, 'mots_par_mois_combines.png')
|
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.set_title('Signes publiés par mois - Comparaison des blogs', fontsize=14, fontweight='bold')
|
||||||
ax.legend(loc='upper left')
|
ax.legend(loc='upper left')
|
||||||
ax.grid(True, alpha=0.3, axis='y')
|
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)))
|
# Améliorer le formatage des dates pour éviter la superposition
|
||||||
plt.xticks([mdates.date2num(m) for m in mois_dates], rotation=45, ha='right')
|
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()
|
plt.tight_layout()
|
||||||
|
|
||||||
graph3_path = os.path.join(graph_dir, 'signes_par_mois_combines.png')
|
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}")
|
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():
|
def main():
|
||||||
"""
|
"""
|
||||||
Fonction principale qui analyse les blogs et génère les pages de statistiques.
|
Fonction principale qui analyse les blogs et génère les pages de statistiques.
|
||||||
"""
|
"""
|
||||||
parser = argparse.ArgumentParser(
|
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(
|
parser.add_argument(
|
||||||
'blogs',
|
'blogs',
|
||||||
nargs='*',
|
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(
|
parser.add_argument(
|
||||||
'--output',
|
'--output',
|
||||||
|
|
@ -1084,6 +1402,11 @@ def main():
|
||||||
type=int,
|
type=int,
|
||||||
help='Objectif de signes par mois (prend le pas sur l\'objectif quotidien et NaNoWriMo)'
|
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()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
|
@ -1099,6 +1422,29 @@ def main():
|
||||||
|
|
||||||
template = env.get_template('templates/html/stats.html.j2')
|
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é
|
# Lister tous les dossiers de blogs si aucun n'est spécifié
|
||||||
if not os.path.exists(sources_dir):
|
if not os.path.exists(sources_dir):
|
||||||
print(f"Erreur: Le dossier {sources_dir} n'existe pas")
|
print(f"Erreur: Le dossier {sources_dir} n'existe pas")
|
||||||
|
|
|
||||||
|
|
@ -14,26 +14,66 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<h2>Vue d'ensemble</h2>
|
<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="stats-grid">
|
||||||
<div class="stat-card">
|
<div class="stat-card">
|
||||||
<h3>Articles publiés</h3>
|
<h3>Articles publiés</h3>
|
||||||
<div class="value">{{nb_articles_total|int|format_number}}</div>
|
<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>
|
||||||
<div class="stat-card blue">
|
<div class="stat-card blue">
|
||||||
<h3>Mots totaux</h3>
|
<h3>Mots totaux</h3>
|
||||||
<div class="value">{{mots_total|int|format_number}}</div>
|
<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>
|
||||||
<div class="stat-card green">
|
<div class="stat-card green">
|
||||||
<h3>Signes (espaces inclus)</h3>
|
<h3>Signes (espaces inclus)</h3>
|
||||||
<div class="value">{{signes_total|int|format_number}}</div>
|
<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>
|
||||||
<div class="stat-card orange">
|
<div class="stat-card orange">
|
||||||
<h3>Temps de lecture total</h3>
|
<h3>Temps de lecture total</h3>
|
||||||
<div class="value">{{temps_lecture_total|format_duree}}</div>
|
<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>
|
||||||
<div class="stat-card purple">
|
<div class="stat-card purple">
|
||||||
<h3>Liens totaux</h3>
|
<h3>Liens totaux</h3>
|
||||||
<div class="value">{{liens_total|int|format_number}}</div>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -115,7 +155,12 @@
|
||||||
<p><strong>Objectif quotidien:</strong> 1667 signes (espaces compris) par jour</p>
|
<p><strong>Objectif quotidien:</strong> 1667 signes (espaces compris) par jour</p>
|
||||||
{% for mois_stats in stats_nanowrimo %}
|
{% for mois_stats in stats_nanowrimo %}
|
||||||
<div class="nanowrimo-month">
|
<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 %}
|
{% if mois_stats.objectif_articles is not none %}
|
||||||
<div class="progress-info">
|
<div class="progress-info">
|
||||||
|
|
@ -198,6 +243,26 @@
|
||||||
(objectif jusqu'à aujourd'hui: {{mois_stats.objectif_jusqu_aujourdhui|int|format_number}} signes)
|
(objectif jusqu'à aujourd'hui: {{mois_stats.objectif_jusqu_aujourdhui|int|format_number}} signes)
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% 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>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,12 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% for mois_stats in stats_nanowrimo_combines %}
|
{% for mois_stats in stats_nanowrimo_combines %}
|
||||||
<div class="nanowrimo-month">
|
<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">
|
<div class="progress-info">
|
||||||
<strong>Signes réalisés (tous blogs):</strong> {{mois_stats.signes_realises|int|format_number}}
|
<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)
|
(objectif jusqu'à aujourd'hui: {{mois_stats.objectif_jusqu_aujourdhui|int|format_number}} signes)
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% 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>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -286,7 +311,12 @@
|
||||||
<p><strong>Objectif quotidien:</strong> 1667 signes (espaces compris) par jour</p>
|
<p><strong>Objectif quotidien:</strong> 1667 signes (espaces compris) par jour</p>
|
||||||
{% for mois_stats in blog_data.stats_nanowrimo %}
|
{% for mois_stats in blog_data.stats_nanowrimo %}
|
||||||
<div class="nanowrimo-month">
|
<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 %}
|
{% if mois_stats.objectif_articles is not none %}
|
||||||
<div class="progress-info">
|
<div class="progress-info">
|
||||||
|
|
@ -369,6 +399,26 @@
|
||||||
(objectif jusqu'à aujourd'hui: {{mois_stats.objectif_jusqu_aujourdhui|int|format_number}} signes)
|
(objectif jusqu'à aujourd'hui: {{mois_stats.objectif_jusqu_aujourdhui|int|format_number}} signes)
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% 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>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue