From cb2f0d1aa329f4a72e91e6e8247bbb7cb04b7d1b Mon Sep 17 00:00:00 2001 From: Tykayn Date: Thu, 27 Feb 2025 22:59:57 +0100 Subject: [PATCH] testing utils, generate better gemini links --- converters.sh | 14 +- linking_articles_prev_next.py | 354 ++++++++++-------- .../lang_en/20250227222248__hey-ho.org | 24 ++ .../20250227222253__salut-les-bidules.org | 24 ++ test_utils.py | 33 ++ utils.py | 45 ++- 6 files changed, 311 insertions(+), 183 deletions(-) create mode 100644 sources/dragonfeu_blog/lang_en/20250227222248__hey-ho.org create mode 100644 sources/dragonfeu_blog/lang_en/20250227222253__salut-les-bidules.org create mode 100644 test_utils.py diff --git a/converters.sh b/converters.sh index f9f9168d..a90b1759 100755 --- a/converters.sh +++ b/converters.sh @@ -112,23 +112,25 @@ for website_name in "${blogs_folders[@]}"; do # générer l'index montrant les posts les plus récents à la suite # conversion des pages statiques - echo "conversion des pages statiques : linking_articles_prev_next.py" + # echo "conversion des pages statiques : linking_articles_prev_next.py" 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 - echo "création des pages de tags : gather_tags_in_json.py" + # echo "création des pages de tags : gather_tags_in_json.py" python3 gather_tags_in_json.py $website_name - echo "copie du style $style_file dans le dossier html" + # echo "copie du style $style_file dans le dossier html" cp $style_file html-websites/$website_name/style.css - echo "copie du script main_script.js dans le dossier html" + # echo "copie du script main_script.js dans le dossier html" cp templates/js/main_script.js html-websites/$website_name/main_script.js # traiter les réductions d'images dans l'inbox - echo "traiter les réductions d'images dans l'inbox : pictures_resize.py" - python3 pictures_resize.py + # echo "traiter les réductions d'images dans l'inbox : pictures_resize.py" + + # TODO uncomment this picture resizer + # python3 pictures_resize.py # régénérer le flux Atom du blog mkdir -p "html-websites/$website_name/feed" diff --git a/linking_articles_prev_next.py b/linking_articles_prev_next.py index 63faea8d..f2882144 100644 --- a/linking_articles_prev_next.py +++ b/linking_articles_prev_next.py @@ -16,8 +16,6 @@ import time # Importer le module time # Démarrer le chronomètre start_time = time.time() # Configs pour tester -generate_linkings_json = True - # Configurer argparse pour prendre le blog en argument @@ -26,14 +24,18 @@ parser.add_argument('blog', type=str, help='Nom du dossier du blog à traiter', parser.add_argument('--run_gemini', type=str, help='Activer ou non la génération des billets gemini', default=True) parser.add_argument('--run_pandoc', type=str, help='Activer ou non la génération des fichiers html', default=True) parser.add_argument('--enable_roam_id_rewrite', type=str, help='Activer ou non la réécriture des liens roam', default=False) +parser.add_argument('--generate_html_pages', type=str, help='Activer ou non la génération des pages html', default=True) +parser.add_argument('--generate_linkings_json', type=str, help='Activer ou non la génération du json des liens entre articles', default=True) parser.add_argument('--force_html_regen', action='store_true', help='Forcer la régénération des fichiers HTML même s\'ils existent déjà') - +parser.add_argument('--rebuild_articles_info_json', type=str, help='Chemin du fichier JSON des articles', default=False) args = parser.parse_args() run_gemini = args.run_gemini run_pandoc = args.run_pandoc +generate_linkings_json = args.generate_linkings_json force_html_regen = args.force_html_regen - +generate_html_pages = args.generate_html_pages +rebuild_articles_info_json = args.rebuild_articles_info_json # TODO check cette fonctionnalité enable_roam_id_rewrite = args.enable_roam_id_rewrite @@ -45,27 +47,39 @@ def get_basename(file_name): directory_pages = f'sources/{args.blog}/' lang_fr = f'sources/{args.blog}/lang_fr' lang_en = f'sources/{args.blog}/lang_en' -directories_to_scan = [directory_pages, lang_fr, lang_en] +# directories_to_scan = [directory_pages, lang_fr, lang_en] +directories_to_scan = [directory_pages] destination_json = f'sources/{args.blog}/build' destination_html = f'html-websites/{args.blog}/' destination_gmi = f'gemini-capsules/{args.blog}/' +# Si rebuild_articles_info_json est True, supprimer le fichier JSON existant +if rebuild_articles_info_json: + print(f"Suppression du fichier JSON existant: {json_file}") + try: + if os.path.exists(json_file): + os.remove(json_file) + print(f"Fichier JSON {json_file} supprimé avec succès") + except Exception as e: + print(f"Erreur lors de la suppression du fichier JSON: {e}") + # Dictionnaire pour stocker les informations des fichiers # Vérifier si le fichier JSON existe déjà json_file = destination_json + '/articles_info.json' +files_dict = {} if os.path.exists(json_file): - print(f"Chargement du fichier JSON existant: {json_file}") + # ----- print(f"Chargement du fichier JSON existant: {json_file}") try: with open(json_file, 'r', encoding='utf-8') as f: files_dict = json.load(f) print(f"Fichier JSON chargé avec succès, {len(files_dict)} articles trouvés") except Exception as e: print(f"Erreur lors du chargement du fichier JSON: {e}") - files_dict = {} + else: - print("Aucun fichier JSON existant trouvé") - files_dict = {} + print(f"Aucun fichier articles_info.json existant trouvé, reconstruction des informations du blog {args.blog}") + @@ -79,159 +93,173 @@ lang_folder = global_config.get('lang_default', 'fr') if generate_linkings_json : - print(f"Génération des liens entre articles pour {count_articles} articles") - print(f"run_pandoc: {run_pandoc}") - print(f"run_gemini: {run_gemini}") + # ----- print(f"Génération des liens entre articles pour {count_articles} articles") + # ----- print(f"run_pandoc: {run_pandoc}") + # ----- print(f"run_gemini: {run_gemini}") article_type = "article" # Parcourir les fichiers du dossier for index, directory in enumerate(directories_to_scan): - if directory == ".." or directory == "build" or directory == "templates" or directory == "img": - continue - # Déterminer le type d'article en fonction du chemin - if directory == '/': - article_type = "page" - else: - article_type = "article" - # Extraire la langue du dossier si elle commence par "lang_" - if directory.split('/')[-1].startswith('lang_'): - lang_folder = directory.split('/')[-1][5:] # Prend les caractères après "lang_" - for file_name in os.listdir(directory): - - # Vérifier si le fichier se termine par une extension supportée - if not (file_name.endswith('.org') or file_name.endswith('.md') or file_name.endswith('.gmi')): + print(f"Traitement du dossier {directory}, {index}/{len(directories_to_scan)}") + for index, subdir in enumerate(os.listdir(directory)): + print(f"Traitement du dossier {directory}/{subdir}, {index}/{len(os.listdir(directory))}") + if subdir == ".." or subdir == "build" or subdir == "templates" or subdir == "img": continue - if file_name.endswith('.org'): - counter+=1 - if force_html_regen and counter % 10 == 0: - print(f"{time.strftime('%H:%M:%S')} : Articles traités : {counter}/{count_articles}") - file_path = os.path.join(directory, file_name) - with open(file_path, "r", encoding="utf-8") as f: - content = f.read() - date_modified = time.ctime(os.path.getmtime(file_path)) - - basename = get_basename(file_name) - date_str, annee, slug = find_year_and_slug_on_filename(basename) - tags = extract_tags_from_file(file_path, global_config['excluded_tags']) - - # Convertir les tags en liste si c'est un set - if isinstance(tags, set): - tags = list(tags) - boom = basename.split('__') - # Convertir le contenu Org en HTML - title = find_first_level1_title(content) - - # Désactiver les warning d'identifiant dupliqué dans la conversion pandoc - content_without_h1 = re.sub(r'^\*.*?$', '', content, count=1, flags=re.MULTILINE) - - gemini_content = '' - html_content = '' - html_content_without_h1 = '' - # 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_time = None - if os.path.exists(html_path): - # Obtenir la date de création du fichier HTML - last_html_build_time = os.path.getctime(html_path) - - # print(f"last_html_build: {last_html_build_time} : {html_path}") - else: - print(f"----------- last_html_build html_path: {html_path} n'existe pas") - # Vérifier l'existence du fichier Gemini pour déterminer last_gemini_build - os.makedirs(os.path.dirname(f"./gemini-capsules/{args.blog}/{annee}"), exist_ok=True) - gemini_path = f"./gemini-capsules/{args.blog}/{annee}/{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: + # Déterminer le type d'article en fonction du chemin + if directory == '/': + article_type = "page" + else: + article_type = "article" + # Extraire la langue du dossier si elle commence par "lang_" + if directory.split('/')[-1].startswith('lang_'): + lang_folder = directory.split('/')[-1][5:] # Prend les caractères après "lang_" + for index, file_name in enumerate(os.listdir(f'{directory}/{subdir}')): + print(f"directory: {subdir}, {article_type}, {file_name}, {index}/{len(os.listdir(f'{directory}/{subdir}'))}") + # Vérifier si le fichier se termine par une extension supportée + if not (file_name.endswith('.org') or file_name.endswith('.md') or file_name.endswith('.gmi')): + continue + if file_name.endswith('.org'): + counter+=1 + print(f"Traitement de l'article {counter}/{count_articles} {file_name}") + file_path = os.path.join(directory, subdir, file_name) + if force_html_regen and counter % 10 == 0: + print(f"{time.strftime('%H:%M:%S')} : Articles traités : {counter}/{count_articles}") - rebuild_this_article_gemini = True + with open(file_path, "r", encoding="utf-8") as f: + content = f.read() + date_modified = time.ctime(os.path.getmtime(file_path)) - # Vérifier si l'article doit être reconstruit en comparant les dates de modification - rebuild_this_article_html = False - if last_html_build_time: - file_modified_time = os.path.getmtime(file_path) - # print(f"--------- file_modified_time: {file_path} : {file_modified_time}") - # Obtenir l'heure de dernière modification du fichier HTML + basename = get_basename(file_name) + date_str, annee, slug = find_year_and_slug_on_filename(basename) + tags = extract_tags_from_file(file_path, global_config['excluded_tags']) - rebuild_this_article_html = file_modified_time > last_html_build_time - # print(f"--------- article modifié après le build de son rendu html: {file_path}, {rebuild_this_article_html}") - else: - # si il n'y a pas de fichier html, on le construit pour la première fois - print('on reconstruit le html de l\'article', file_name) - - rebuild_this_article_html = True - - if rebuild_this_article_html: - rebuild_counter += 1 + # Convertir les tags en liste si c'est un set + if isinstance(tags, set): + tags = list(tags) + boom = basename.split('__') + # Convertir le contenu Org en HTML + title = find_first_level1_title(content) + # Désactiver les warning d'identifiant dupliqué dans la conversion pandoc + content_without_h1 = re.sub(r'^\*.*?$', '', content, count=1, flags=re.MULTILINE) - # Garder le contenu HTML existant si déjà présent - if f"{annee}/{slug}" in files_dict and 'html_content' in files_dict[f"{annee}/{slug}"]: - print('on reprend le contenu html existant') - if len(files_dict[f"{annee}/{slug}"]['html_content']) > 0: - html_content = files_dict[f"{annee}/{slug}"]['html_content'] - if len(files_dict[f"{annee}/{slug}"]['html_content_without_h1']) > 0: - html_content_without_h1 = files_dict[f"{annee}/{slug}"]['html_content_without_h1'] + gemini_content = '' + html_content = '' + html_content_without_h1 = '' + # 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_time = None + if os.path.exists(html_path): + # Obtenir la date de création du fichier HTML + last_html_build_time = os.path.getctime(html_path) + + # # ----- print(f"last_html_build: {last_html_build_time} : {html_path}") else: - html_content_without_h1 = re.sub(r'

.*?

', '', html_content) + # ----- print(f"----------- last_html_build html_path: {html_path} n'existe pas") + # Vérifier l'existence du fichier Gemini pour déterminer last_gemini_build + os.makedirs(os.path.dirname(f"./html_websites/{args.blog}/{annee}"), exist_ok=True) + os.makedirs(os.path.dirname(f"./gemini-capsules/{args.blog}/{annee}"), exist_ok=True) + gemini_path = f"./gemini-capsules/{args.blog}/{annee}/{slug}.gmi" - if run_pandoc and rebuild_this_article_html or force_html_regen: - # convertir le contenu d'article org vers html - # print(f"\033[91mBRRRRRRRRRRRRR pandoc time {time.strftime('%H:%M:%S')} : Conversion de {file_name} en html\033[0m") - print(f"\033[91m.\033[0m", end='', flush=True) + 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 + print(f"rebuild_this_article_gemini: {rebuild_this_article_gemini}") - html_content = pypandoc.convert_text(content_without_h1, 'html', format='org') - pandoc_runs_counter += 1 - else: - html_content = content_without_h1 - - if run_gemini and rebuild_this_article_gemini: - print('-----------on régénère le gemini') - # convertir le contenu d'article org vers gmi pour la capsule gemini - gemini_content = org_to_gmi(content_without_h1) - print('len(gemini_content)', len(gemini_content)) - else: - print('-----------on ne régénère pas le gemini') + # Vérifier si l'article doit être reconstruit en comparant les dates de modification + rebuild_this_article_html = False + if last_html_build_time: + file_modified_time = os.path.getmtime(file_path) + # # ----- print(f"--------- file_modified_time: {file_path} : {file_modified_time}") + # Obtenir l'heure de dernière modification du fichier HTML + + rebuild_this_article_html = file_modified_time > last_html_build_time + # # ----- print(f"--------- article modifié après le build de son rendu html: {file_path}, {rebuild_this_article_html}") + else: + # si il n'y a pas de fichier html, on le construit pour la première fois + # ----- print('on reconstruit le html de l\'article', file_name) + rebuild_this_article_html = True + + if rebuild_this_article_html: + rebuild_counter += 1 - files_dict[f"{annee}/{slug}"] = { - 'path': file_path, - 'basename': basename, - 'roam_id': find_org_roam_id(content), - 'slug': f"{slug}/", - 'slug_with_year': f"{annee}/{slug}", - 'date': boom[0], - 'lang': lang_folder, - 'article_type': article_type, - 'date_modified' : date_modified, - 'first_picture_url' : get_first_picture_url(content), - 'date_formattee': datetime.strptime(date_str, '%Y%m%d%H%M%S').strftime('%d %B %Y à %H:%M:%S') if len(date_str) == 14 else datetime.strptime(date_str, '%Y%m%dT%H%M%S').strftime('%d %B %Y à %H:%M:%S') if len(date_str) == 15 else datetime.strptime(date_str, '%Y-%m-%d').strftime('%d %B %Y'), - 'annee': annee, - 'tags': tags, - 'title': title, - 'next': None, - 'previous': None, - 'last_html_build': last_html_build_time, - 'last_gemini_build': last_gemini_build, - 'org_content': content, # Contenu Org original - 'html_content_without_h1': html_content_without_h1, # Contenu HTML converti sans le titre de premier niveau - 'html_content': html_content, # Contenu first_picture_urlHTML converti - 'gemini_content': gemini_content, # Contenu gemini - } + # Garder le contenu HTML existant si déjà présent + if f"{annee}/{slug}" in files_dict and 'html_content' in files_dict[f"{annee}/{slug}"]: + # ----- print('on reprend le contenu html existant') + if len(files_dict[f"{annee}/{slug}"]['html_content']) > 0: + html_content = files_dict[f"{annee}/{slug}"]['html_content'] + if len(files_dict[f"{annee}/{slug}"]['html_content_without_h1']) > 0: + html_content_without_h1 = files_dict[f"{annee}/{slug}"]['html_content_without_h1'] + else: + html_content_without_h1 = re.sub(r'

.*?

', '', html_content) - print(f"======= Nombre d'articles reconstruits: {rebuild_counter}") - print(f"======= Nombre de runs de pandoc: {pandoc_runs_counter}") + + + if run_pandoc and rebuild_this_article_html or force_html_regen: + # convertir le contenu d'article org vers html + # # ----- print(f"\033[91mBRRRRRRRRRRRRR pandoc time {time.strftime('%H:%M:%S')} : Conversion de {file_name} en html\033[0m") + # ----- print(f"\033[91m.\033[0m", end='', flush=True) + + html_content = pypandoc.convert_text(content_without_h1, 'html', format='org') + pandoc_runs_counter += 1 + else: + html_content = content_without_h1 + + # if run_gemini and rebuild_this_article_gemini: + # # ----- print('-----------on régénère le gemini') + # # convertir le contenu d'article org vers gmi pour la capsule gemini + # gemini_content = org_to_gmi(content_without_h1) + # # ----- print('len(gemini_content)', len(gemini_content)) + # else: + # print('-----------on ne régénère pas le gemini') + if rebuild_this_article_gemini: + gemini_content = org_to_gmi(content_without_h1) + + files_dict[f"{annee}/{slug}"] = { + 'path': file_path, + 'basename': basename, + 'roam_id': find_org_roam_id(content), + 'slug': f"{slug}/", + 'slug_with_year': f"{annee}/{slug}", + 'date': boom[0], + 'lang': lang_folder, + 'article_type': article_type, + 'date_modified' : date_modified, + 'first_picture_url' : get_first_picture_url(content), + 'date_formattee': datetime.strptime(date_str, '%Y%m%d%H%M%S').strftime('%d %B %Y à %H:%M:%S') if len(date_str) == 14 else datetime.strptime(date_str, '%Y%m%dT%H%M%S').strftime('%d %B %Y à %H:%M:%S') if len(date_str) == 15 else datetime.strptime(date_str, '%Y-%m-%d').strftime('%d %B %Y'), + 'annee': annee, + 'tags': tags, + 'title': title, + 'next': None, + 'previous': None, + 'last_html_build': last_html_build_time, + 'last_gemini_build': last_gemini_build, + 'org_content': content, # Contenu Org original + 'html_content_without_h1': html_content_without_h1, # Contenu HTML converti sans le titre de premier niveau + 'html_content': html_content, # Contenu first_picture_urlHTML converti + 'gemini_content': gemini_content, + 'gemini_file_path': f"{annee}/{slug}.gmi", + # Contenu gemini + } + + # ----- print(f"======= Nombre d'articles reconstruits: {rebuild_counter}") + # ----- print(f"======= Nombre de runs de pandoc: {pandoc_runs_counter}") + +else: + print(f"Pas de génération des liens entre articles") # Trier les basenames par ordre décroissant sorted_basenames = sorted(files_dict.keys(), reverse=True) -print(len(sorted_basenames), 'articles trouvés') +# ----- print(len(sorted_basenames), 'articles trouvés') template_content = get_blog_template_conf(args.blog) @@ -245,7 +273,7 @@ if enable_roam_id_rewrite: rewritten_html_content = html_content for roam_id, slug in articles_roam_id_to_slugs.items(): if roam_id is not None and isinstance(rewritten_html_content, str) and roam_id in rewritten_html_content: - print(f'{roam_id} -> {slug}') + # ----- print(f'{roam_id} -> {slug}') rewritten_html_content = rewritten_html_content.replace(f'href="#{roam_id}"', f'href="{template_content["NDD"]}/{slug}"') info['rewritten_roam_links_html'] = rewritten_html_content @@ -262,19 +290,19 @@ for i in range(len(sorted_basenames)): os.makedirs(destination_json, exist_ok=True) -json_file=destination_json+'/articles_info.json' + # sauver le json de tous les articles et pages if pandoc_runs_counter > 0 or not os.path.exists(json_file): - print(f"\033[91m Les articles ont changé, Génération du json {json_file} \033[0m") + # ----- print(f"\033[91m Les articles ont changé, Génération du json {json_file} \033[0m") with open( json_file, 'w', encoding='utf-8') as json_file: files_dict_serialized = json.dumps(files_dict, ensure_ascii=False, indent=4) json_file.write(files_dict_serialized) -print(f"Nombre d'articles trouvés : {len(sorted_basenames)}") +# ----- print(f"Nombre d'articles trouvés : {len(sorted_basenames)}") count_articles_updated = 0 current_year = datetime.now().year for basename, info in files_dict.items(): @@ -283,7 +311,7 @@ for basename, info in files_dict.items(): if date_str > f'{current_year}0101': count_articles_updated += 1 -print(f"Nombre d'articles mis à jour après le 01 01 {current_year} : {count_articles_updated}") +# ----- print(f"Nombre d'articles mis à jour après le 01 01 {current_year} : {count_articles_updated}") def generate_blog_index(json_file, template_file, output_file): @@ -319,7 +347,7 @@ def generate_blog_index(json_file, template_file, output_file): gmi_list_articles = '' for basename, article in files_dict.items(): - gmi_list_articles += f"\n=> {article['slug_with_year']}.gmi " + gmi_list_articles += f"=> {article['slug']}.gmi {article['title']}\n" output_index_gmi = f""" # {template_content['BLOG_TITLE']} @@ -345,7 +373,7 @@ Index des {len(files_dict.items())} articles: --------------------- Pages: -=> index.gmi +=> index.gmi Index """ gmi_index_file=destination_gmi+'index.gmi' @@ -353,13 +381,13 @@ Pages: # Écrire le fichier de sortie en html et en gmi with open(output_file, 'w', encoding='utf-8') as f: f.write(output_index_html) - print(f"Page d'index générée dans {output_file}") + # ----- print(f"Page d'index générée dans {output_file}") os.makedirs(os.path.dirname(gmi_index_file), exist_ok=True) - print('gmi_index_file', gmi_index_file) + # ----- print('gmi_index_file', gmi_index_file) with open(gmi_index_file, 'w', encoding='utf-8') as f: f.write(output_index_gmi) - print(f"Page d'index gemini générée dans {gmi_index_file}") + # ----- print(f"Page d'index gemini générée dans {gmi_index_file}") # Générer la page d'index seulement si des articles ont été convertis @@ -367,9 +395,9 @@ Pages: # Appel de la fonction pour générer la page d'index generate_blog_index(destination_json + '/articles_info.json', 'templates/html/index.html.jinja', destination_html + 'index.html') -print(f"\033[91m index régénéré {destination_html}index.html \033[0m") +# ----- print(f"\033[91m index régénéré {destination_html}index.html \033[0m") # else: - # print("Aucun article n'a été converti, la page d'index n'est pas régénérée") + # # ----- print("Aucun article n'a été converti, la page d'index n'est pas régénérée") def generate_article_pages(json_file, template_file, output_dir): @@ -381,7 +409,7 @@ def generate_article_pages(json_file, template_file, output_dir): :param output_dir: Répertoire de sortie pour les fichiers HTML. """ counter_gemini = 0 - print('generate_article_pages: ouverture du json') + # ----- print('generate_article_pages: ouverture du json') # Charger les données JSON try: # Charger les données JSON @@ -395,7 +423,7 @@ def generate_article_pages(json_file, template_file, output_dir): # Générer les pages pour chaque article for article in articles_info.values(): - print('article', article['title']) + print('----------------------- article', article['title']) if article['first_picture_url']: template_content['OG_IMAGE'] = article['first_picture_url'] @@ -422,7 +450,7 @@ def generate_article_pages(json_file, template_file, output_dir): with open(output_file, 'w', encoding='utf-8') as f: f.write(output_html) - print(f"Génération de la page HTML pour {article['title']}") + print(f"Génération de la page gemini pour {article['title']}") if 'gemini_content' in article and len(article['gemini_content']) > 0: # Construire le chemin de sortie gmi en fonction du slug avec l'année save_gemini_file(args.blog, article) @@ -430,8 +458,8 @@ def generate_article_pages(json_file, template_file, output_dir): else: print(f"----------- on ne génère pas le gemini pour {article['slug']}") - print('generate_article_pages: fin de génération de l index') - print(f"Nombre d'articles générés en gemini: {counter_gemini}") + # ----- print('generate_article_pages: fin de génération de l index') + # ----- print(f"Nombre d'articles générés en gemini: {counter_gemini}") except FileNotFoundError: print(f"Erreur : Le fichier JSON {json_file} n'a pas été trouvé") return @@ -439,7 +467,7 @@ def generate_article_pages(json_file, template_file, output_dir): print(f"Erreur : Le fichier JSON {json_file} est mal formaté") return except Exception as e: - print(f"Erreur lors de la lecture du fichier JSON : {str(e)}") + print(f"Erreur lors de la lecture du fichier JSON : {json_file} {str(e)}") return with open(json_file, 'r', encoding='utf-8') as f: @@ -452,7 +480,7 @@ generate_article_pages(destination_json + '/articles_info.json', 'templates/html # À la fin du script, calculer et afficher le temps d'exécution execution_time = time.time() - start_time -print(f"Temps d'exécution : {execution_time:.2f} secondes") +# ----- print(f"Temps d'exécution : {execution_time:.2f} secondes") diff --git a/sources/dragonfeu_blog/lang_en/20250227222248__hey-ho.org b/sources/dragonfeu_blog/lang_en/20250227222248__hey-ho.org new file mode 100644 index 00000000..1cc37a9b --- /dev/null +++ b/sources/dragonfeu_blog/lang_en/20250227222248__hey-ho.org @@ -0,0 +1,24 @@ +:PROPERTIES: +:ID: a6cebff3-cc5c-4252-9fde-41409d3d838e +:END: + +#+title: Hey ho +#+post_ID: +#+post_slug: hey-ho + + +#+post_url: https://www.ciperbliss.com/2025/hey-ho +#+post_title: Hey ho +#+post_tags: +#+post_series: +#+post_type: post +#+post_status: publish +#+post_picture: +#+post_date_published: <2025-02-27 22:22:48> +#+post_date_modified: <2025-02-27 22:22:48> +#+post_index_page_roam_id: a6cebff3-cc5c-4252-9fde-41409d3d838e +#+BLOG: dragonfeu_blog + +* Hey ho + + diff --git a/sources/dragonfeu_blog/lang_en/20250227222253__salut-les-bidules.org b/sources/dragonfeu_blog/lang_en/20250227222253__salut-les-bidules.org new file mode 100644 index 00000000..288f19cb --- /dev/null +++ b/sources/dragonfeu_blog/lang_en/20250227222253__salut-les-bidules.org @@ -0,0 +1,24 @@ +:PROPERTIES: +:ID: 3260e0fa-fe0b-456f-9e30-c969a0d2e1bd +:END: + +#+title: Salut les bidules +#+post_ID: +#+post_slug: salut-les-bidules + + +#+post_url: https://www.ciperbliss.com/2025/salut-les-bidules +#+post_title: Salut les bidules +#+post_tags: +#+post_series: +#+post_type: post +#+post_status: publish +#+post_picture: +#+post_date_published: <2025-02-27 22:22:53> +#+post_date_modified: <2025-02-27 22:22:53> +#+post_index_page_roam_id: 3260e0fa-fe0b-456f-9e30-c969a0d2e1bd +#+BLOG: dragonfeu_blog + +* Salut les bidules + + diff --git a/test_utils.py b/test_utils.py new file mode 100644 index 00000000..76268163 --- /dev/null +++ b/test_utils.py @@ -0,0 +1,33 @@ +import pytest +from utils import slugify_title + +def test_slugify_title_with_accents(): + """Test que slugify conserve les accents francophones""" + title = "Rétrospective du cul" + expected = "rétrospective-du-cul" + result = slugify_title(title) + assert result == expected, f"Expected '{expected}' but got '{result}'" + +def test_slugify_title_various_cases(): + """Test slugify avec différents cas de figure""" + test_cases = [ + ("Hello World!", "hello-world"), + ("L'été est là", "l-été-est-là"), + ("100% Bio", "100-bio"), + ("Spécial & Unique", "spécial-unique"), + ("Multiple Spaces", "multiple-spaces"), + ("émission télé", "émission-télé"), + ("TOUT EN MAJUSCULES", "tout-en-majuscules"), + ("mix OF cases", "mix-of-cases"), + ("points...multiples...", "points-multiples"), + ("Voilà l'été", "voilà-l-été"), + ("Être ou ne pas être", "être-ou-ne-pas-être"), + ("Ça c'est génial !", "ça-c-est-génial"), + ] + + for input_title, expected in test_cases: + result = slugify_title(input_title) + assert result == expected, f"Pour '{input_title}', attendu '{expected}' mais reçu '{result}'" + +if __name__ == '__main__': + pytest.main([__file__]) \ No newline at end of file diff --git a/utils.py b/utils.py index ddc23f1a..5d1bcbf7 100644 --- a/utils.py +++ b/utils.py @@ -284,20 +284,37 @@ def remove_hint_html(text): return re.sub(pattern, replacement, text, flags=re.DOTALL) -def slugify_title(title_text): +def slugify_title(title): """ - Transforme un titre en un slug valide. - - :param title_text: Titre en texte (str). - :return: Slug en minuscules avec des tirets (str). + Convertit un titre en slug URL-friendly en conservant les accents. + - Convertit en minuscules + - Conserve les accents francophones + - Remplace les caractères spéciaux par des tirets + - Supprime les tirets multiples + + Args: + title (str): Le titre à convertir + + Returns: + str: Le slug généré """ - title_text = unicodedata.normalize('NFKD', title_text).encode('ascii', 'ignore').decode('ascii') - title_text = title_text.lower() - title_text = re.sub(r'[^a-z0-9\s-]', '', title_text) - title_text = re.sub(r'\s+', '-', title_text) - title_text = re.sub(r'-+', '-', title_text) - title_text = title_text.strip('-') - return title_text + # Conversion en minuscules + title = title.lower() + + # Liste des caractères autorisés (inclut les accents francophones) + # On garde a-z, 0-9, les accents français, les tirets + allowed_chars = r'[^a-zàâäéèêëîïôöùûüçñ0-9-]' + + # Remplacer les caractères non autorisés par des tirets + title = re.sub(allowed_chars, '-', title) + + # Supprimer les tirets en début et fin + title = title.strip('-') + + # Remplacer les tirets multiples par un seul + title = re.sub(r'-+', '-', title) + + return title def find_slug_in_file_basename(file_basename) -> str: """ @@ -522,7 +539,7 @@ def convert_org_to_gemini(org_content): def save_gemini_file(blog, article): annee = article['annee'] - slug = article['slug'] + slug = slugify_title(article['title']) title = article['title'] gemini_content = article['gemini_content'] gemini_file_path = article['gemini_file_path'] @@ -539,7 +556,7 @@ def save_gemini_file(blog, article): # Sauvegarde du contenu GMI dans un fichier try: # Créer le dossier parent s'il n'existe pas - os.makedirs(os.path.dirname(gemini_file_path), exist_ok=True) + # os.makedirs(os.path.dirname(gemini_file_path), exist_ok=True) with open(gemini_file_path, 'w', encoding='utf-8') as f: f.write( f'# {website_title} \n ## {title} \n----------------\n {gemini_content} \n ----------------\n ## Liens\n=> index.gmi')