2025-02-19 16:24:20 +01:00
#!/bin/python3
2025-02-19 16:29:20 +01:00
# trouver les articles précédents et suivants
2025-02-19 16:24:20 +01:00
from utils import *
from website_config import configs_sites
import os
import json
import re
2025-02-19 16:29:20 +01:00
import argparse
2025-02-19 22:39:11 +01:00
import pypandoc
from jinja2 import Environment , FileSystemLoader
2025-02-20 14:53:59 +01:00
import time # Importer le module time
# Démarrer le chronomètre
start_time = time . time ( )
2025-02-21 23:40:35 +01:00
# Configs pour tester
2025-02-20 17:14:53 +01:00
generate_linkings_json = True
generate_articles = True
2025-02-21 23:40:35 +01:00
2025-02-19 16:29:20 +01:00
# Configurer argparse pour prendre le blog en argument
parser = argparse . ArgumentParser ( description = ' Générer une liste des derniers articles de blog. ' )
2025-02-23 19:12:15 +01:00
parser . add_argument ( ' blog ' , type = str , help = ' Nom du dossier du blog à traiter ' , default = ' cipherbliss_blog ' )
parser . add_argument ( ' --run_gemini ' , type = str , help = ' Activer ou non la génération des billets gemini ' , default = False )
parser . add_argument ( ' --run_pandoc ' , type = str , help = ' Activer ou non la génération des fichiers html ' , default = True )
2025-02-23 20:23:38 +01:00
parser . add_argument ( ' --enable_roam_id_rewrite ' , type = str , help = ' Activer ou non la réécriture des liens roam ' , default = False )
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à ' )
2025-02-22 18:55:01 +01:00
2025-02-19 16:29:20 +01:00
args = parser . parse_args ( )
2025-02-23 19:12:15 +01:00
run_gemini = args . run_gemini
run_pandoc = args . run_pandoc
2025-02-23 20:23:38 +01:00
force_html_regen = args . force_html_regen
# TODO check cette fonctionnalité
enable_roam_id_rewrite = args . enable_roam_id_rewrite
2025-02-19 16:24:20 +01:00
# Fonction pour extraire le basename d'un fichier
def get_basename ( file_name ) :
return os . path . splitext ( file_name ) [ 0 ]
# Chemin du dossier contenant les fichiers orgmode
2025-02-19 16:29:20 +01:00
directory = f ' sources/ { args . blog } /lang_fr '
destination_json = f ' sources/ { args . blog } /build '
2025-02-19 22:39:11 +01:00
destination_html = f ' html-websites/ { args . blog } / '
2025-02-21 23:40:35 +01:00
destination_gmi = f ' gemini-capsules/ { args . blog } / '
2025-02-19 16:24:20 +01:00
# Dictionnaire pour stocker les informations des fichiers
files_dict = { }
2025-02-20 14:53:59 +01:00
def get_first_picture_url ( content ) :
# Utiliser une expression régulière pour trouver la première URL d'image dans le contenu
pattern = r ' \ [ \ [(.*?) \ ] \ ] '
match = re . search ( pattern , content )
if match :
return match . group ( 1 )
else :
return None
2025-02-21 23:40:35 +01:00
def org_to_gmi ( org_text : str , output_filename_slug : str ) - > str :
"""
Convertit un texte au format Org en un fichier au format GMI ( Gemini )
en utilisant pypandoc .
Args :
- org_text ( str ) : Le texte au format Org à convertir .
- output_file ( str ) : Chemin du fichier de sortie au format GMI , sans avoir à préciser l ' extension.
"""
output = """
# mock land output
== == == == == =
blah blah blah
- - - - - - - - - - - - - - - - -
Tykayn blog mock content
- - - - - - - - - - - - - - - - -
Navigation :
= > accueil . gmi Accueil
= > a - propos . gmi à propos
"""
# Conversion du texte Org en GMI via Pandoc
try :
output = pypandoc . convert_text ( org_text , ' markdown ' , format = ' org ' )
except RuntimeError as e :
print ( f " Erreur de conversion : { e } " )
return
# Sauvegarde du contenu GMI dans un fichier
try :
with open ( destination_gmi + ' / ' + output_filename_slug + ' .gmi ' , ' w ' , encoding = ' utf-8 ' ) as f :
f . write ( output )
2025-02-23 19:46:10 +01:00
print ( f " Fichier GMI sauvegardé avec succès : { output_filename_slug } " )
2025-02-21 23:40:35 +01:00
except OSError as e :
print ( f " Erreur lors de la sauvegarde du fichier : { e } " )
return output
2025-02-20 17:14:53 +01:00
if generate_linkings_json :
2025-02-23 19:12:15 +01:00
count_articles = len ( os . listdir ( directory ) )
counter = 0
2025-02-23 20:09:39 +01:00
rebuild_counter = 0
pandoc_runs_counter = 0
2025-02-23 19:12:15 +01:00
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 } " )
2025-02-20 17:14:53 +01:00
# Parcourir les fichiers du dossier
for file_name in os . listdir ( directory ) :
if file_name . endswith ( ' .org ' ) :
2025-02-23 19:12:15 +01:00
counter + = 1
2025-02-23 20:23:38 +01:00
if force_html_regen and counter % 10 == 0 :
2025-02-23 19:12:15 +01:00
print ( f " { time . strftime ( ' % H: % M: % S ' ) } : Articles traités : { counter } / { count_articles } " )
2025-02-20 17:14:53 +01:00
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 )
2025-02-21 23:40:35 +01:00
gemini_content = ' '
html_content = ' '
2025-02-23 16:59:59 +01:00
# Vérifier l'existence du fichier HTML pour déterminer last_html_build
2025-02-23 20:09:39 +01:00
html_path = f " html-websites/ { args . blog } / { annee } / { slug } /index.html "
last_html_build_time = None
2025-02-23 16:59:59 +01:00
if os . path . exists ( html_path ) :
2025-02-23 20:09:39 +01:00
# Obtenir la date de création du fichier HTML
last_html_build_time = os . path . getctime ( html_path )
2025-02-23 20:23:38 +01:00
# print(f"last_html_build: {last_html_build_time} : {html_path}")
2025-02-23 20:09:39 +01:00
else :
print ( f " ----------- last_html_build html_path: { html_path } n ' existe pas " )
2025-02-23 16:59:59 +01:00
# 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 :
2025-02-23 20:09:39 +01:00
2025-02-23 16:59:59 +01:00
rebuild_this_article_gemini = True
# Vérifier si l'article doit être reconstruit en comparant les dates de modification
rebuild_this_article_html = False
2025-02-23 20:09:39 +01:00
if last_html_build_time :
2025-02-23 16:59:59 +01:00
file_modified_time = os . path . getmtime ( file_path )
2025-02-23 20:23:38 +01:00
# print(f"--------- file_modified_time: {file_path} : {file_modified_time}")
2025-02-23 20:09:39 +01:00
# 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}")
2025-02-23 16:59:59 +01:00
else :
2025-02-23 20:09:39 +01:00
# 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 )
2025-02-23 16:59:59 +01:00
rebuild_this_article_html = True
2025-02-23 20:09:39 +01:00
if rebuild_this_article_html :
rebuild_counter + = 1
2025-02-21 23:40:35 +01:00
2025-02-23 20:23:38 +01:00
if run_pandoc and rebuild_this_article_html or force_html_regen :
2025-02-21 23:40:35 +01:00
# convertir le contenu d'article org vers html
2025-02-23 20:09:39 +01:00
print ( f " BRRRRRRRRRRRR pandoc time { time . strftime ( ' % H: % M: % S ' ) } : Conversion de { file_name } en html " )
2025-02-21 23:40:35 +01:00
html_content = pypandoc . convert_text ( content_without_h1 , ' html ' , format = ' org ' )
2025-02-23 20:09:39 +01:00
pandoc_runs_counter + = 1
2025-02-20 17:14:53 +01:00
else :
html_content = content_without_h1
2025-02-23 16:59:59 +01:00
if run_gemini and rebuild_this_article_gemini :
2025-02-21 23:40:35 +01:00
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 )
2025-02-20 17:14:53 +01:00
2025-02-23 16:59:59 +01:00
2025-02-20 17:14:53 +01:00
files_dict [ f " { annee } / { slug } " ] = {
' path ' : file_path ,
' basename ' : basename ,
2025-02-23 15:50:56 +01:00
' roam_id ' : find_org_roam_id ( content ) ,
2025-02-20 17:14:53 +01:00
' slug ' : f " { slug } / " ,
' slug_with_year ' : f " { annee } / { slug } " ,
' date ' : boom [ 0 ] ,
' 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 %d T % 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 ,
2025-02-23 20:09:39 +01:00
' last_html_build ' : last_html_build_time ,
2025-02-23 17:04:43 +01:00
' last_gemini_build ' : last_gemini_build ,
2025-02-20 17:14:53 +01:00
' org_content ' : content , # Contenu Org original
' html_content_without_h1 ' : re . sub ( r ' <h1>.*?</h1> ' , ' ' , html_content ) , # Contenu HTML converti sans le titre de premier niveau
2025-02-23 19:46:10 +01:00
' html_content ' : html_content # Contenu first_picture_urlHTML converti
2025-02-20 17:14:53 +01:00
}
2025-02-19 16:24:20 +01:00
2025-02-23 20:09:39 +01:00
print ( f " ======= Nombre d ' articles reconstruits: { rebuild_counter } " )
print ( f " ======= Nombre de runs de pandoc: { pandoc_runs_counter } " )
2025-02-19 16:24:20 +01:00
# Trier les basenames par ordre décroissant
sorted_basenames = sorted ( files_dict . keys ( ) , reverse = True )
2025-02-20 14:53:59 +01:00
print ( len ( sorted_basenames ) , ' articles trouvés ' )
2025-02-19 16:24:20 +01:00
2025-02-23 15:50:56 +01:00
template_content = get_blog_template_conf ( args . blog )
# Dictionnaire des identifiants roam qui mène à un slug d'article pour réécrire les références
articles_roam_id_to_slugs = { info [ ' roam_id ' ] : slug for slug , info in files_dict . items ( ) }
# Parcourir les articles de files_dict et ajouter une clé rewritten_roam_links_html là où un lien vers un identifiant roam est trouvé dans le html_content
2025-02-23 20:23:38 +01:00
if enable_roam_id_rewrite :
for slug , info in files_dict . items ( ) :
html_content = info [ ' html_content ' ]
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 } ' )
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
2025-02-23 15:50:56 +01:00
# Ajouter les infos des articles suivant et précédent dans la liste des articles
2025-02-19 16:24:20 +01:00
for i in range ( len ( sorted_basenames ) ) :
basename = sorted_basenames [ i ]
2025-02-23 15:50:56 +01:00
2025-02-19 16:24:20 +01:00
if i > 0 :
files_dict [ basename ] [ ' previous ' ] = sorted_basenames [ i - 1 ]
if i < len ( sorted_basenames ) - 1 :
files_dict [ basename ] [ ' next ' ] = sorted_basenames [ i + 1 ]
2025-02-23 15:50:56 +01:00
os . makedirs ( destination_json , exist_ok = True )
json_file = destination_json + ' /articles_info.json '
with open ( json_file , ' w ' , encoding = ' utf-8 ' ) as json_file :
2025-02-19 16:24:20 +01:00
files_dict_serialized = json . dumps ( files_dict , ensure_ascii = False , indent = 4 )
json_file . write ( files_dict_serialized )
2025-02-19 22:39:11 +01:00
print ( f " Nombre d ' articles trouvés : { len ( sorted_basenames ) } " )
count_articles_updated = 0
for basename , info in files_dict . items ( ) :
date_str = info [ ' date ' ]
2025-02-23 17:37:53 +01:00
current_year = datetime . now ( ) . year
if date_str > f ' { current_year } 0101 ' :
2025-02-19 22:39:11 +01:00
count_articles_updated + = 1
2025-02-23 17:37:53 +01:00
print ( f " Nombre d ' articles mis à jour après le 01 01 { current_year } : { count_articles_updated } " )
2025-02-19 22:39:11 +01:00
def generate_blog_index ( json_file , template_file , output_file ) :
"""
Génère la page d ' index du blog à partir des informations JSON et d ' un template Jinja2 .
: param json_file : Chemin du fichier JSON contenant les informations des articles .
: param template_file : Chemin du fichier template Jinja2 .
: param output_file : Chemin du fichier HTML de sortie .
"""
# Charger les données JSON
with open ( json_file , ' r ' , encoding = ' utf-8 ' ) as f :
articles_info = json . load ( f )
# Trier les articles par date (ou par slug) et prendre les 10 derniers
2025-02-20 17:14:53 +01:00
sorted_articles = sorted ( articles_info . values ( ) , key = lambda x : x [ ' date ' ] , reverse = True ) [ : global_config [ ' posts_per_page ' ] ]
2025-02-19 22:39:11 +01:00
# Configurer Jinja2
env = Environment ( loader = FileSystemLoader ( ' . ' ) )
template = env . get_template ( template_file )
2025-02-20 17:14:53 +01:00
articles_others = sorted ( articles_info . values ( ) , key = lambda x : x [ ' date ' ] , reverse = True ) [ 10 : ]
2025-02-23 15:50:56 +01:00
template_content = get_blog_template_conf ( args . blog )
2025-02-19 22:39:11 +01:00
# Rendre le template avec les données
2025-02-21 23:40:35 +01:00
output_index_html = template . render (
2025-02-23 15:50:56 +01:00
template_content = template_content ,
2025-02-19 22:39:11 +01:00
articles = sorted_articles [ : global_config [ ' posts_per_page ' ] ] ,
2025-02-20 17:14:53 +01:00
articles_others = articles_others
2025-02-19 22:39:11 +01:00
)
2025-02-21 23:40:35 +01:00
gmi_list_articles = ' '
for basename , article in files_dict . items ( ) :
2025-02-21 23:41:04 +01:00
gmi_list_articles + = f " \n => { article [ ' slug ' ] } .gmi "
2025-02-23 15:50:56 +01:00
2025-02-21 23:40:35 +01:00
output_index_gmi = f """
2025-02-23 15:50:56 +01:00
# {template_content['BLOG_TITLE']}
2025-02-21 23:40:35 +01:00
== == == == == == == == == == == == == == == == == == == == == == == =
2025-02-19 22:39:11 +01:00
2025-02-23 15:50:56 +01:00
{ template_content [ ' BANNIERE_ENTETE ' ] }
Par { template_content [ ' AUTHOR ' ] }
2025-02-21 23:40:35 +01:00
Dernière mise à jour : { datetime . now ( ) }
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2025-02-23 15:50:56 +01:00
{ template_content [ ' DESCRIPTION ' ] }
2025-02-21 23:40:35 +01:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2025-02-23 15:50:56 +01:00
{ template_content [ ' SITE_ICON ' ] }
2025-02-21 23:40:35 +01:00
- - - - - - - - - - - - - - - - - - - - -
Index des { len ( files_dict . items ( ) ) } articles :
{ gmi_list_articles }
- - - - - - - - - - - - - - - - - - - - -
Pages :
= > index . gmi
"""
gmi_index_file = destination_gmi + ' index.gmi '
# É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 } " )
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 } " )
2025-02-19 22:39:11 +01:00
# Appel de la fonction pour générer la page d'index
2025-02-19 23:10:28 +01:00
generate_blog_index ( destination_json + ' /articles_info.json ' , ' templates/html/index.html.jinja ' , destination_html + ' /index.html ' )
def generate_article_pages ( json_file , template_file , output_dir ) :
"""
Génère les pages HTML pour chaque article à partir des informations JSON et d ' un template Jinja2.
: param json_file : Chemin du fichier JSON contenant les informations des articles .
: param template_file : Chemin du fichier template Jinja2 .
: param output_dir : Répertoire de sortie pour les fichiers HTML .
"""
2025-02-20 17:14:53 +01:00
print ( ' generate_article_pages: ouverture du json ' )
2025-02-19 23:10:28 +01:00
# Charger les données JSON
with open ( json_file , ' r ' , encoding = ' utf-8 ' ) as f :
articles_info = json . load ( f )
2025-02-20 14:53:59 +01:00
# Configurer Jinja2
env = Environment ( loader = FileSystemLoader ( ' . ' ) )
template = env . get_template ( template_file )
2025-02-23 19:46:10 +01:00
template_content = get_blog_template_conf ( args . blog )
2025-02-20 14:53:59 +01:00
# Générer les pages pour chaque article
for article in articles_info . values ( ) :
2025-02-23 19:46:10 +01:00
if article [ ' first_picture_url ' ] :
template_content [ ' OG_IMAGE ' ] = article [ ' first_picture_url ' ]
else :
template_content [ ' OG_IMAGE ' ] = template_content [ ' SITE_ICON ' ]
2025-02-20 14:53:59 +01:00
output_html = template . render (
2025-02-23 19:46:10 +01:00
template_content = template_content ,
2025-02-20 14:53:59 +01:00
article = article ,
all_articles = articles_info
)
2025-02-19 23:10:28 +01:00
2025-02-20 14:53:59 +01:00
# Construire le chemin de sortie en fonction du slug avec l'année
output_subdir = os . path . join ( output_dir , article [ ' slug_with_year ' ] )
2025-02-20 16:30:08 +01:00
2025-02-20 14:53:59 +01:00
os . makedirs ( output_subdir , exist_ok = True )
output_file = os . path . join ( output_subdir , " index.html " )
2025-02-19 23:10:28 +01:00
2025-02-20 14:53:59 +01:00
# print(output_file)
# Écrire le fichier de sortie
with open ( output_file , ' w ' , encoding = ' utf-8 ' ) as f :
f . write ( output_html )
2025-02-20 17:14:53 +01:00
print ( ' generate_article_pages: fin de génération de l index ' )
2025-02-19 23:10:28 +01:00
2025-02-20 17:14:53 +01:00
if generate_articles :
2025-02-19 23:10:28 +01:00
# Appel de la fonction pour générer les pages des articles
2025-02-20 17:14:53 +01:00
generate_article_pages ( destination_json + ' /articles_info.json ' , ' templates/html/article.html.jinja ' , destination_html )
2025-02-19 23:10:28 +01:00
2025-02-20 14:53:59 +01:00
# À 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 " )
2025-02-19 23:10:28 +01:00