2025-08-30 18:57:27 +02:00
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Script pour analyser les fautes d ' orthographe et de grammaire dans un fichier livre.org
et générer un rapport par chapitre .
Ce script :
1. Lit le fichier livre . org
2. Extrait le texte par chapitre
3. Analyse les fautes d ' orthographe et de grammaire dans chaque chapitre
4. Génère un rapport détaillé des erreurs trouvées
"""
import re
import os
import csv
import argparse
2025-08-31 22:37:24 +02:00
# Vérifier si les modules nécessaires sont disponibles
try :
import language_tool_python
LANGUAGE_TOOL_AVAILABLE = True
except ImportError :
print ( " AVERTISSEMENT: Module language_tool_python non disponible. La vérification grammaticale sera désactivée. " )
LANGUAGE_TOOL_AVAILABLE = False
try :
from spellchecker import SpellChecker
SPELLCHECKER_AVAILABLE = True
except ImportError :
print ( " AVERTISSEMENT: Module spellchecker non disponible. La vérification orthographique sera désactivée. " )
SPELLCHECKER_AVAILABLE = False
2025-08-30 18:57:27 +02:00
# Définir les arguments en ligne de commande
parser = argparse . ArgumentParser ( description = ' Analyser les fautes d \' orthographe et de grammaire dans un fichier Org-mode. ' )
parser . add_argument ( ' dossier ' , nargs = ' ? ' , help = ' Le chemin du dossier contenant le fichier livre.org. Si aucun dossier n \' est spécifié, le dossier courant sera utilisé. ' , default = os . getcwd ( ) )
args = parser . parse_args ( )
# Chemin vers le fichier livre.org
fichier_livre = f " { args . dossier } /livre.org "
def extract_chapters ( file_path ) :
"""
Extrait les chapitres d ' un fichier org-mode.
Retourne un dictionnaire avec les titres des chapitres comme clés et leur contenu comme valeurs .
"""
with open ( file_path , ' r ' , encoding = ' utf-8 ' ) as file :
content = file . read ( )
# Diviser le contenu par chapitres (lignes commençant par **)
chapter_pattern = r ' ^ \ * \ * (.*?)$(.*?)(?=^ \ * \ * | \ Z) '
chapters = re . findall ( chapter_pattern , content , re . MULTILINE | re . DOTALL )
chapter_dict = { }
for title , content in chapters :
# Nettoyer le titre (supprimer ": title:" s'il existe)
clean_title = re . sub ( r ' \ s*: \ s*title \ s*: ' , ' ' , title ) . strip ( )
# Nettoyer le contenu
clean_content = clean_chapter_content ( content )
chapter_dict [ clean_title ] = clean_content
return chapter_dict
def clean_chapter_content ( content ) :
"""
Nettoie le contenu d ' un chapitre en supprimant les commentaires et les balises org-mode.
"""
# Supprimer les blocs de commentaires
content = re . sub ( r ' # \ +begin_comment.*?# \ +end_comment ' , ' ' , content , flags = re . DOTALL | re . IGNORECASE )
# Supprimer les lignes de métadonnées (commençant par #+)
content = re . sub ( r ' ^ \ s*# \ +.*$ ' , ' ' , content , flags = re . MULTILINE )
# Supprimer les sous-titres (lignes commençant par ***)
content = re . sub ( r ' ^ \ s* \ * \ * \ *.*$ ' , ' ' , content , flags = re . MULTILINE )
# Supprimer les liens org-mode [[...][...]] et [[...]]
content = re . sub ( r ' \ [ \ [.*? \ ] \ ](?: \ [.*? \ ])? ' , ' ' , content )
# Supprimer les lignes vides multiples
content = re . sub ( r ' \ n \ s* \ n ' , ' \n \n ' , content )
return content . strip ( )
2025-08-30 22:41:51 +02:00
def load_custom_dictionary ( file_path ) :
"""
Charge le dictionnaire personnalisé à partir d ' un fichier texte.
Retourne un ensemble de mots à considérer comme corrects .
"""
custom_words = set ( )
2025-08-30 23:10:43 +02:00
2025-08-30 22:41:51 +02:00
# Vérifier si le fichier existe
if os . path . exists ( file_path ) :
with open ( file_path , ' r ' , encoding = ' utf-8 ' ) as file :
for line in file :
# Ignorer les lignes vides et les commentaires
line = line . strip ( )
if line and not line . startswith ( ' # ' ) :
custom_words . add ( line . lower ( ) )
2025-08-30 23:10:43 +02:00
2025-08-30 22:41:51 +02:00
return custom_words
def check_spelling ( text , lang = ' fr ' , custom_dict_path = ' dictionnaire_personnalise.txt ' ) :
2025-08-30 18:57:27 +02:00
"""
Vérifie l ' orthographe d ' un texte et retourne les mots mal orthographiés .
2025-08-30 22:41:51 +02:00
Utilise un dictionnaire personnalisé pour exclure certains mots de la vérification .
2025-08-30 18:57:27 +02:00
"""
2025-08-31 22:37:24 +02:00
# Vérifier si le module spellchecker est disponible
if not SPELLCHECKER_AVAILABLE :
print ( " Vérification orthographique désactivée car le module spellchecker n ' est pas disponible. " )
return [ ]
try :
spell = SpellChecker ( language = lang )
2025-08-30 23:10:43 +02:00
2025-08-31 22:37:24 +02:00
# Charger le dictionnaire personnalisé
custom_words = load_custom_dictionary ( custom_dict_path )
2025-08-30 23:10:43 +02:00
2025-08-31 22:37:24 +02:00
# Ajouter les mots du dictionnaire personnalisé au dictionnaire du vérificateur
if custom_words :
spell . word_frequency . load_words ( custom_words )
2025-08-30 23:10:43 +02:00
2025-08-31 22:37:24 +02:00
# Diviser le texte en mots
words = re . findall ( r ' \ b \ w+ \ b ' , text . lower ( ) )
2025-08-30 23:10:43 +02:00
2025-08-31 22:37:24 +02:00
# Trouver les mots mal orthographiés
misspelled = spell . unknown ( words )
2025-08-30 23:10:43 +02:00
2025-08-31 22:37:24 +02:00
# Créer un dictionnaire avec les mots mal orthographiés et leurs suggestions
spelling_errors = [ ]
for word in misspelled :
# Vérifier si le mot est dans le dictionnaire personnalisé
if word in custom_words :
continue
2025-08-30 23:10:43 +02:00
2025-08-31 22:37:24 +02:00
# Obtenir les suggestions de correction
suggestions = spell . candidates ( word )
# Limiter à 5 suggestions maximum
suggestions_list = list ( suggestions ) if suggestions is not None else [ ]
suggestions_list = suggestions_list [ : 5 ]
# Trouver toutes les occurrences du mot dans le texte original
for match in re . finditer ( r ' \ b ' + re . escape ( word ) + r ' \ b ' , text , re . IGNORECASE ) :
# Extraire le contexte autour du mot
word_start = match . start ( )
word_end = match . end ( )
# Trouver les limites des lignes contenant le mot
line_start = text . rfind ( ' \n ' , 0 , word_start ) + 1 if text . rfind ( ' \n ' , 0 , word_start ) > = 0 else 0
line_end = text . find ( ' \n ' , word_end ) if text . find ( ' \n ' , word_end ) > = 0 else len ( text )
# Extraire les lignes précédentes et suivantes pour plus de contexte
prev_line_start = text . rfind ( ' \n ' , 0 , line_start - 1 ) + 1 if text . rfind ( ' \n ' , 0 , line_start - 1 ) > = 0 else 0
next_line_end = text . find ( ' \n ' , line_end + 1 ) if text . find ( ' \n ' , line_end + 1 ) > = 0 else len ( text )
# Créer le contexte standard et étendu
context = text [ line_start : line_end ]
extended_context = text [ prev_line_start : next_line_end ]
# Calculer les offsets pour les contextes
context_offset = word_start - line_start
extended_offset = word_start - prev_line_start
spelling_errors . append ( {
' word ' : word ,
' context ' : context ,
' extended_context ' : extended_context ,
' context_offset ' : context_offset ,
' extended_offset ' : extended_offset ,
' suggestions ' : suggestions_list
} )
2025-08-30 23:10:43 +02:00
2025-08-31 22:37:24 +02:00
return spelling_errors
except Exception as e :
print ( f " Erreur lors de la vérification orthographique: { str ( e ) } " )
return [ ]
2025-08-30 18:57:27 +02:00
def check_grammar ( text , lang = ' fr ' ) :
"""
Vérifie la grammaire d ' un texte et retourne les erreurs grammaticales.
"""
2025-08-31 22:37:24 +02:00
# Vérifier si le module language_tool_python est disponible
if not LANGUAGE_TOOL_AVAILABLE :
print ( " Vérification grammaticale désactivée car le module language_tool_python n ' est pas disponible. " )
return [ ]
try :
# Initialiser l'outil de vérification grammaticale
tool = language_tool_python . LanguageTool ( lang )
# Vérifier le texte
matches = tool . check ( text )
# Créer une liste d'erreurs grammaticales
grammar_errors = [ ]
for match in matches :
# Ignorer les erreurs d'orthographe (déjà traitées par le vérificateur d'orthographe)
if match . ruleId . startswith ( ' MORFOLOGIK_RULE ' ) :
continue
# Extraire plus de contexte autour de l'erreur
error_start = match . offset
error_end = match . offset + match . errorLength
# Trouver les limites des lignes contenant l'erreur
line_start = text . rfind ( ' \n ' , 0 , error_start ) + 1 if text . rfind ( ' \n ' , 0 , error_start ) > = 0 else 0
line_end = text . find ( ' \n ' , error_end ) if text . find ( ' \n ' , error_end ) > = 0 else len ( text )
# Extraire les lignes précédentes et suivantes pour plus de contexte
prev_line_start = text . rfind ( ' \n ' , 0 , line_start - 1 ) + 1 if text . rfind ( ' \n ' , 0 , line_start - 1 ) > = 0 else 0
next_line_end = text . find ( ' \n ' , line_end + 1 ) if text . find ( ' \n ' , line_end + 1 ) > = 0 else len ( text )
# Créer le contexte étendu
extended_context = text [ prev_line_start : next_line_end ]
# Ajuster les offsets pour le contexte étendu
extended_offset = error_start - prev_line_start
error = {
' message ' : match . message ,
' context ' : match . context ,
' extended_context ' : extended_context ,
' suggestions ' : match . replacements ,
' offset ' : match . offset ,
' extended_offset ' : extended_offset ,
' length ' : match . errorLength ,
' rule ' : match . ruleId
}
grammar_errors . append ( error )
# Fermer l'outil pour libérer les ressources
tool . close ( )
return grammar_errors
except Exception as e :
print ( f " Erreur lors de la vérification grammaticale: { str ( e ) } " )
return [ ]
2025-08-30 18:57:27 +02:00
def generate_error_report ( chapters , output_path ) :
"""
Génère un rapport des erreurs d ' orthographe et de grammaire par chapitre.
"""
with open ( output_path , ' w ' , encoding = ' utf-8 ' ) as report_file :
report_file . write ( " # Rapport d ' analyse orthographique et grammaticale \n \n " )
total_spelling_errors = 0
total_grammar_errors = 0
for chapter_title , chapter_content in chapters . items ( ) :
report_file . write ( f " ## Chapitre: { chapter_title } \n \n " )
# Vérifier l'orthographe
spelling_errors = check_spelling ( chapter_content )
# Vérifier la grammaire
grammar_errors = check_grammar ( chapter_content )
# Mettre à jour les totaux
total_spelling_errors + = len ( spelling_errors )
total_grammar_errors + = len ( grammar_errors )
# Écrire les erreurs d'orthographe
report_file . write ( " ### Erreurs d ' orthographe \n \n " )
if spelling_errors :
2025-08-31 22:37:24 +02:00
# Regrouper les erreurs par mot
errors_by_word = { }
for error in spelling_errors :
word = error [ ' word ' ]
if word not in errors_by_word :
errors_by_word [ word ] = {
' suggestions ' : error [ ' suggestions ' ] ,
' occurrences ' : [ ]
}
errors_by_word [ word ] [ ' occurrences ' ] . append ( {
' context ' : error [ ' context ' ] ,
' extended_context ' : error [ ' extended_context ' ] ,
' context_offset ' : error [ ' context_offset ' ] ,
' extended_offset ' : error [ ' extended_offset ' ]
} )
# Écrire les erreurs regroupées par mot
for word , data in errors_by_word . items ( ) :
suggestions_str = " , " . join ( data [ ' suggestions ' ] ) if data [ ' suggestions ' ] else " Aucune suggestion "
2025-08-30 18:57:27 +02:00
report_file . write ( f " - ** { word } **: { suggestions_str } \n " )
2025-08-31 22:37:24 +02:00
# Ajouter les contextes pour chaque occurrence
for i , occurrence in enumerate ( data [ ' occurrences ' ] ) :
# Mettre en évidence le mot dans le contexte
context = occurrence [ ' context ' ]
offset = occurrence [ ' context_offset ' ]
highlighted_context = context [ : offset ] + f " ** { word } ** " + context [ offset + len ( word ) : ]
# Ajouter le contexte étendu
extended_context = occurrence [ ' extended_context ' ]
report_file . write ( f " - **Occurrence { i + 1 } **: \n " )
report_file . write ( f " - **Contexte**: { highlighted_context } \n " )
report_file . write ( f " - **Contexte étendu**: ``` \n { extended_context } \n ``` \n " )
2025-08-30 18:57:27 +02:00
else :
report_file . write ( " Aucune erreur d ' orthographe détectée. \n " )
report_file . write ( " \n " )
# Écrire les erreurs grammaticales
report_file . write ( " ### Erreurs grammaticales \n \n " )
if grammar_errors :
2025-08-31 22:37:24 +02:00
for i , error in enumerate ( grammar_errors ) :
2025-08-30 18:57:27 +02:00
suggestions_str = " , " . join ( error [ ' suggestions ' ] [ : 5 ] ) if error [ ' suggestions ' ] else " Aucune suggestion "
2025-08-31 22:37:24 +02:00
# Mettre en évidence l'erreur dans le contexte
2025-08-30 18:57:27 +02:00
context = error [ ' context ' ] . replace ( error [ ' context ' ] [ error [ ' offset ' ] : error [ ' offset ' ] + error [ ' length ' ] ] ,
f " ** { error [ ' context ' ] [ error [ ' offset ' ] : error [ ' offset ' ] + error [ ' length ' ] ] } ** " )
2025-08-31 22:37:24 +02:00
report_file . write ( f " - **Erreur { i + 1 } **: { error [ ' message ' ] } \n " )
2025-08-30 18:57:27 +02:00
report_file . write ( f " - **Contexte**: { context } \n " )
2025-08-31 22:37:24 +02:00
# Ajouter le contexte étendu
if ' extended_context ' in error :
report_file . write ( f " - **Contexte étendu**: ``` \n { error [ ' extended_context ' ] } \n ``` \n " )
2025-08-30 18:57:27 +02:00
report_file . write ( f " - **Suggestions**: { suggestions_str } \n \n " )
else :
report_file . write ( " Aucune erreur grammaticale détectée. \n " )
report_file . write ( " \n --- \n \n " )
# Écrire le résumé
report_file . write ( " ## Résumé \n \n " )
report_file . write ( f " - **Nombre total de chapitres analysés**: { len ( chapters ) } \n " )
report_file . write ( f " - **Nombre total d ' erreurs d ' orthographe**: { total_spelling_errors } \n " )
report_file . write ( f " - **Nombre total d ' erreurs grammaticales**: { total_grammar_errors } \n " )
print ( f " Rapport d ' erreurs généré: { output_path } " )
def save_to_csv ( chapters , output_path ) :
"""
Sauvegarde un résumé des erreurs dans un fichier CSV .
"""
with open ( output_path , ' w ' , newline = ' ' , encoding = ' utf-8 ' ) as csvfile :
writer = csv . writer ( csvfile )
writer . writerow ( [ ' Chapitre ' , ' Erreurs d \' orthographe ' , ' Erreurs grammaticales ' , ' Total ' ] )
for chapter_title , chapter_content in chapters . items ( ) :
spelling_errors = check_spelling ( chapter_content )
grammar_errors = check_grammar ( chapter_content )
total_errors = len ( spelling_errors ) + len ( grammar_errors )
writer . writerow ( [ chapter_title , len ( spelling_errors ) , len ( grammar_errors ) , total_errors ] )
print ( f " Résumé des erreurs sauvegardé dans { output_path } " )
def main ( ) :
print ( f " Analyse du fichier: { fichier_livre } " )
# Extraire les chapitres
chapters = extract_chapters ( fichier_livre )
print ( f " Nombre de chapitres trouvés: { len ( chapters ) } " )
# Définir les chemins de sortie
report_output = f " { args . dossier } /rapport_orthographe_grammaire.md "
csv_output = f " { args . dossier } /resume_erreurs.csv "
# Générer le rapport d'erreurs
generate_error_report ( chapters , report_output )
# Sauvegarder le résumé en CSV
save_to_csv ( chapters , csv_output )
print ( " Analyse orthographique et grammaticale terminée avec succès! " )
if __name__ == " __main__ " :
main ( )