From 759f30f6285c6d70a3ed980198b81c8de0545f0a Mon Sep 17 00:00:00 2001 From: Tykayn Date: Sun, 23 Feb 2025 16:59:59 +0100 Subject: [PATCH] script de stats --- atom_generate.py | 127 ++++++++++------------------------ converters.sh | 1 + linking_articles_prev_next.py | 34 ++++++++- new_article.py | 17 +++-- stats.py | 41 +++++++++++ utils.py | 70 ++++++++++++++++++- website_config.py | 15 ++-- 7 files changed, 196 insertions(+), 109 deletions(-) create mode 100644 stats.py diff --git a/atom_generate.py b/atom_generate.py index e4d48241..11887fb8 100755 --- a/atom_generate.py +++ b/atom_generate.py @@ -1,111 +1,56 @@ import os -import re +import json from datetime import datetime - -# Chemin du dossier source import argparse - -from utils import find_first_level1_title, find_year_and_slug_on_filename, find_extract_in_content_org +from utils import get_blog_template_conf from website_config import configs_sites # Configuration des arguments de la ligne de commande -parser = argparse.ArgumentParser(description="Générer un nouvel article en mode orgmode.") +parser = argparse.ArgumentParser(description="Générer un flux Atom des articles.") parser.add_argument("blog", help="Le nom du dossier de blog.") - args = parser.parse_args() -website_ndd = configs_sites[args.blog]['NDD'] -blog = 'sources/'+args.blog+'/lang_fr/' +template_content = get_blog_template_conf(args.blog) +website_ndd = template_content['NDD'] -# Expression régulière pour extraire la date du contenu de l'article -date_regex = re.compile(r"\b(\d{14})\b") -date_regex_org = re.compile(r"\b(\d{4}-\d{2}-\d{2})\b") +# Charger les données du fichier articles_info.json +json_file = f'sources/{args.blog}/build/articles_info.json' +with open(json_file, 'r', encoding='utf-8') as f: + articles_info = json.load(f) -# Liste des fichiers org-mode trouvés -org_files = [] - -limit_articles_feed=1000 -count_articles=0 - -# Parcourt le dossier source à la recherche de fichiers org-mode -for root, dirs, files in os.walk(blog): - for file in files: - if file.endswith(".org"): - date_str, annee, slug = find_year_and_slug_on_filename(file) - with open(os.path.join(root, file), "r", encoding="utf-8") as f: - content = f.read() - extract = find_extract_in_content_org(content) - count_articles+=1 - match = date_regex_org.search(date_str) - if match: - date = datetime.strptime(match.group(1), "%Y-%m-%d") - org_files.append((date, os.path.join(root, file), annee, slug,extract)) - - if count_articles > limit_articles_feed: - break - if count_articles > limit_articles_feed: - break - -org_files.sort(reverse=True) +# Trier les articles par date décroissante +sorted_articles = sorted(articles_info.values(), key=lambda x: x['date'], reverse=True) # Génération du flux Atom -atom_feed = {"title": "Flux Atom des articles de "+args.blog, - "link": f"{website_ndd}/feed", - "updated": org_files[0][0], - "entries": []} - -for date, file, annee, slug, extract in org_files: - # Parse le fichier org-mode pour extraire le titre, la description et la date de publication - with open(file, "r", encoding="utf-8") as f: - content = f.read() - title = find_first_level1_title(content) - description = title - # published = date_str - # Ajoute l'article au flux Atom - atom_entry = {"title": title, - "summary": extract, - "link": f"{website_ndd}/{annee}/{slug}", - "published": date - } - atom_feed["entries"].append(atom_entry) - -# Enregistrement du flux Atom dans un fichier XML -# Le flux Atom doit contenir: -# - Un ID unique pour le flux et chaque entrée -# - Une balise author avec name et email -# - Les dates au format ISO 8601 avec timezone -# - Un lien self vers le fichier XML -with open(f"index_{args.blog}.xml", "w", encoding="utf-8") as f: +with open(f"html-websites/{args.blog}/feed/index.xml", "w", encoding="utf-8") as f: f.write('\n') f.write('\n') - f.write(f' {atom_feed["title"]}\n') - f.write(f' \n') - f.write(f' {atom_feed["updated"]}\n') + f.write(f' Flux Atom des articles de {args.blog}\n') + f.write(f' \n') + f.write(f' {datetime.fromisoformat(sorted_articles[0]["date"]).strftime("%Y-%m-%dT%H:%M:%S+00:00")}\n') f.write(' tag:' + website_ndd + ',2023:/feed\n') f.write(' \n') - f.write(' Auteur du blog\n') - f.write(' auteur@example.com\n') + f.write(f' {configs_sites[args.blog]["AUTHOR"]}\n') + f.write(f' {configs_sites[args.blog]["EMAIL"]}\n') f.write(' \n') f.write(f' \n') - for entry in atom_feed["entries"]: - slug_id = entry["title"].lower().replace(" ", "-").replace("'", "-").replace("--", "-") - with open(file, "r", encoding="utf-8") as article_file: - article_content = article_file.read() - - f.write(' \n') - f.write(f' tag:{website_ndd},2023:{entry["link"]}\n') - f.write(f' {entry["title"]}\n') - f.write(f' \n') - f.write(' \n') - f.write(f' {entry["summary"]}\n') - f.write(f' {entry["published"].strftime("%Y-%m-%dT%H:%M:%S+00:00")}\n') - f.write(f' {entry["published"].strftime("%Y-%m-%dT%H:%M:%S+00:00")}\n') - f.write(' \n') - f.write(f" {configs_sites[args.blog]['AUTHOR']}\n") - f.write(f" {configs_sites[args.blog]['EMAIL']}\n") - f.write(' \n') - f.write(' \n') - f.write('') + # Boucle des articles + for article in sorted_articles: + f.write(' \n') + f.write(f' tag:{website_ndd},2023:{article["slug"]}\n') + f.write(f' {article["title"]}\n') + f.write(f' \n') + f.write(' \n') + f.write(f' {article.get("extract", "")}\n') + f.write(f' {datetime.fromisoformat(article["date"]).strftime("%Y-%m-%dT%H:%M:%S+00:00")}\n') + f.write(f' {datetime.fromisoformat(article["date"]).strftime("%Y-%m-%dT%H:%M:%S+00:00")}\n') + f.write(' \n') + f.write(f' {configs_sites[args.blog]["AUTHOR"]}\n') + f.write(f' {configs_sites[args.blog]["EMAIL"]}\n') + f.write(' \n') + f.write(' \n') + + f.write('') diff --git a/converters.sh b/converters.sh index 046ee045..e373d882 100755 --- a/converters.sh +++ b/converters.sh @@ -87,6 +87,7 @@ for website_name in "${blogs_folders[@]}"; do # conversion des pages statiques python3 linking_articles_prev_next.py $website_name + # créer les pages de tags à partir des infos de tag trouvées dans les fichiers org python3 gather_tags_in_json.py $website_name diff --git a/linking_articles_prev_next.py b/linking_articles_prev_next.py index 629881c9..dcb0eea1 100644 --- a/linking_articles_prev_next.py +++ b/linking_articles_prev_next.py @@ -121,18 +121,47 @@ if generate_linkings_json : gemini_content = '' html_content = '' + # Vérifier l'existence du fichier HTML pour déterminer last_html_build + html_path = f"html_websites/{args.blog}/{annee}/{slug}/index.html" + last_html_build = None + if os.path.exists(html_path): + last_html_build = time.ctime(os.path.getmtime(html_path)) + # Vérifier l'existence du fichier Gemini pour déterminer last_gemini_build + gemini_path = f"gemini-capsules/{args.blog}/{slug}.gmi" + last_gemini_build = None + rebuild_this_article_gemini = False + if os.path.exists(gemini_path): + last_gemini_build = time.ctime(os.path.getmtime(gemini_path)) + # Vérifier si l'article doit être reconstruit en comparant les dates de modification + if last_gemini_build: + file_modified_time = os.path.getmtime(file_path) + last_build_time = time.mktime(time.strptime(last_gemini_build)) + rebuild_this_article_gemini = file_modified_time > last_build_time + else: + rebuild_this_article_gemini = True - if run_pandoc: + # Vérifier si l'article doit être reconstruit en comparant les dates de modification + rebuild_this_article_html = False + if last_html_build: + file_modified_time = os.path.getmtime(file_path) + last_build_time = time.mktime(time.strptime(last_html_build)) + rebuild_this_article_html = file_modified_time > last_build_time + else: + rebuild_this_article_html = True + + if run_pandoc and rebuild_this_article_html: # convertir le contenu d'article org vers html html_content = pypandoc.convert_text(content_without_h1, 'html', format='org') else: html_content = content_without_h1 - if run_gemini: + if run_gemini and rebuild_this_article_gemini: os.makedirs(destination_gmi, exist_ok=True) # convertir le contenu d'article org vers gmi pour la capsule gemini gemini_content = org_to_gmi(content_without_h1, slug) + + files_dict[f"{annee}/{slug}"] = { 'path': file_path, 'basename': basename, @@ -148,6 +177,7 @@ if generate_linkings_json : 'title': title, 'next': None, 'previous': None, + 'last_html_build': last_html_build 'org_content': content, # Contenu Org original 'html_content_without_h1': re.sub(r'

.*?

', '', html_content), # Contenu HTML converti sans le titre de premier niveau 'html_content': html_content # Contenu HTML converti diff --git a/new_article.py b/new_article.py index 24b5cb0d..1dfada87 100644 --- a/new_article.py +++ b/new_article.py @@ -56,7 +56,6 @@ if not os.path.exists(blog_path): exit(1) -uuid_value='' # Génération du nom de fichier org avec la date et le slug now = datetime.now() # date_string = now.strftime("%Y-%m-%d") @@ -80,24 +79,30 @@ import uuid def create_uuid_property(): uuid_value = uuid.uuid4() - return f":PROPERTIES:\n:ID: {uuid_value}\n:END:\n" + return uuid_value # Écriture du fichier org with open(filename, "w") as f: + uuid = create_uuid_property() f.write(f""" -#+title: {slug} +:PROPERTIES: +:ID: {uuid} +:END: + + +#+title: {args.title} #+post_ID: #+post_slug: organisation-de-taches-orgmode #+post_url: https://www.ciperbliss.com/{now.year}/{slug} #+post_title: {args.title} #+post_tags: -#+post_series: +#+post_series: #+post_type: post #+post_status: publish #+post_date_published: <{date_string_full}> #+post_date_modified: <{date_string_full}> -#+post_index_page_roam_id: {uuid_value} -#+BLOG: cipherbliss_blog {args.blog_dir} +#+post_index_page_roam_id: {uuid} +#+BLOG: {args.blog_dir} * {args.title} diff --git a/stats.py b/stats.py new file mode 100644 index 00000000..90b4a534 --- /dev/null +++ b/stats.py @@ -0,0 +1,41 @@ +#!/bin/python3 +# Générer des statistiques sur tous les sites web + +from utils import get_stats_on_all_websites +from website_config import configs_sites + +def main(): + """ + Fonction principale qui génère les statistiques pour tous les sites web configurés + """ + print("Génération des statistiques pour tous les sites web...") + lecture_mots_par_minute = 150 + # Récupérer les statistiques pour tous les sites + stats = get_stats_on_all_websites() + + # Afficher les statistiques pour chaque site + for site_name, site_stats in stats.items(): + print(f"\n=== Statistiques pour {site_name} ===") + print(f"Nombre total d'articles: {site_stats['nb_articles']}") + # Formater le nombre de mots avec séparateur de milliers + mots_formatte = f"{site_stats['nb_mots']:,}".replace(',', ' ') + # Calculer le temps de lecture (150 mots/minute) + + temps_lecture = site_stats['nb_mots'] / lecture_mots_par_minute + heures = int(temps_lecture // 60) + minutes = int(temps_lecture % 60) + + # Si le temps de lecture dépasse 48h, convertir en jours + if heures >= 48: + jours = heures // 24 + heures = heures % 24 + temps_lecture_str = f"{jours}j {heures}h {minutes}min" + else: + temps_lecture_str = f"{heures}h {minutes}min" if heures > 0 else f"{minutes}min" + + print(f"Nombre total de mots: {mots_formatte}") + print(f"Temps de lecture estimé: {temps_lecture_str} (base: {lecture_mots_par_minute} mots/min)") + print(f"Dernier article publié: {site_stats['dernier_article']}") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/utils.py b/utils.py index b45ca969..cc057043 100644 --- a/utils.py +++ b/utils.py @@ -291,8 +291,6 @@ def find_slug_in_file_basename(file_basename) -> str: if len(splitted) > 1: slug = splitted[len(splitted)-1] - # final_slug=slug.replace("_cipherbliss_blog_","") - # final_slug=final_slug.replace("_blog_cil_gometz_","") slug=enlever_premier_tiret_ou_underscore(slug) slug = f"{year}/{slug}" @@ -302,7 +300,73 @@ def find_slug_in_file_basename(file_basename) -> str: return slug return None - +def get_stats_on_all_websites(): + """ + Retourne des statistiques sur tous les sites web dans le dossier sources/. + Pour chaque site, compte le nombre d'articles .org et trouve l'article le plus récent. + + :return: Dictionnaire avec les stats par site + """ + stats = {} + base_dir = "sources" + + # Parcourir tous les dossiers de sites dans sources/ + for site in os.listdir(base_dir): + site_path = os.path.join(base_dir, site) + + if not os.path.isdir(site_path): + continue + + # Initialiser les stats pour ce site + stats[site] = { + 'nb_articles': 0, + 'nb_mots': 0, + 'dernier_article': None, + 'date_dernier_article': None + } + + # Chercher les articles .org dans lang_fr et lang_en + for lang in ['lang_fr', 'lang_en']: + lang_path = os.path.join(site_path, lang) + + if not os.path.exists(lang_path): + continue + + # Lister tous les fichiers .org + org_files = [f for f in os.listdir(lang_path) if f.endswith('.org')] + stats[site]['nb_articles'] += len(org_files) + # Calculer le nombre total de mots pour ce dossier de langue + total_mots = 0 + for org_file in org_files: + file_path = os.path.join(lang_path, org_file) + try: + with open(file_path, 'r', encoding='utf-8') as f: + contenu = f.read() + # Compter les mots en divisant par les espaces + total_mots += len(contenu.split()) + except Exception as e: + print(f"Erreur lors de la lecture de {file_path}: {e}") + + # Ajouter ou incrémenter le compteur de mots dans les stats + stats[site]['nb_mots'] += total_mots + + # Trouver le fichier le plus récent + for org_file in org_files: + file_path = os.path.join(lang_path, org_file) + mod_time = os.path.getmtime(file_path) + + if (stats[site]['date_dernier_article'] is None or + mod_time > stats[site]['date_dernier_article']): + stats[site]['date_dernier_article'] = mod_time + stats[site]['dernier_article'] = file_path + + # Convertir le timestamp en date lisible + if stats[site]['date_dernier_article']: + stats[site]['date_dernier_article'] = datetime.fromtimestamp( + stats[site]['date_dernier_article'] + ).strftime('%Y-%m-%d %H:%M:%S') + + return stats def convert_org_to_html(org_file, output_html_file): """ diff --git a/website_config.py b/website_config.py index 4b7e877e..42b9e04e 100644 --- a/website_config.py +++ b/website_config.py @@ -229,19 +229,20 @@ default_config = { "NAVIGATION": """ """, "BANNIERE_ENTETE": "https://www.cipherbliss.com/banner.jpg", "BANNIERE_ENTETE_ALT": "Bannière par défaut", - "SERIES" : { - "SERIE_1" : { - "TITLE" : "Série 1", - "ARTICLES" : [ - "2024/article-1" : { - "TITLE" : "Article 1", - "slug" : "2024/article-1", + "SERIES": { + "SERIE_1": { + "TITLE": "Série 1", + "ARTICLES": [ + { + "TITLE": "Article 1", + "slug": "2024/article-1" } ] }