book-generator-orgmode/analyse_orthographe_grammaire.py

373 lines
16 KiB
Python
Raw Permalink Normal View History

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()
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
# 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
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.
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()