page corrections ajout de boutons
This commit is contained in:
parent
7095f9633b
commit
1713aa30b5
7 changed files with 297 additions and 44 deletions
134
CONTEXT_MATCHING_FIX.md
Normal file
134
CONTEXT_MATCHING_FIX.md
Normal file
|
@ -0,0 +1,134 @@
|
|||
# Correction API - Context Matching Fix
|
||||
|
||||
## Problème résolu
|
||||
|
||||
L'API de corrections (`api_corrections.py`) ne parvenait pas à trouver le contexte des erreurs grammaticales dans les chapitres. Spécifiquement, lorsqu'un utilisateur tentait d'appliquer une correction avec le payload suivant :
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "apply_correction",
|
||||
"error_type": "grammar",
|
||||
"chapter": "Chapitre 1",
|
||||
"error_index": 2,
|
||||
"correction": "voir :"
|
||||
}
|
||||
```
|
||||
|
||||
L'API retournait une erreur 404 avec le message "Contexte non trouvé dans le chapitre".
|
||||
|
||||
## Cause du problème
|
||||
|
||||
Le problème était dû à deux facteurs principaux :
|
||||
|
||||
1. **Recherche de contexte trop stricte** : L'API utilisait une correspondance exacte pour trouver le contexte de l'erreur dans le chapitre, sans tenir compte des variations possibles dans les espaces, la ponctuation ou le formatage.
|
||||
|
||||
2. **Différences entre le rapport et le fichier source** : Le contexte stocké dans le rapport d'erreurs pouvait différer légèrement du texte réel dans le fichier `livre.org`, notamment en ce qui concerne les espaces et la troncature du texte.
|
||||
|
||||
## Solution implémentée
|
||||
|
||||
### 1. Amélioration de la recherche de contexte
|
||||
|
||||
Nous avons implémenté une approche de recherche de contexte à plusieurs niveaux, du plus strict au plus flexible :
|
||||
|
||||
```python
|
||||
# 1. Échapper les caractères spéciaux dans le contexte
|
||||
escaped_context = re.escape(context)
|
||||
# 2. Remplacer les espaces échappées par un motif qui accepte plusieurs espaces
|
||||
flexible_context = escaped_context.replace('\\ ', '\\s+')
|
||||
# 3. Créer un motif qui permet des variations
|
||||
flexible_pattern = flexible_context
|
||||
|
||||
# Essayer d'abord avec le motif flexible
|
||||
context_match = re.search(flexible_pattern, chapter_content)
|
||||
```
|
||||
|
||||
### 2. Stratégies de fallback
|
||||
|
||||
Si la première approche échoue, nous utilisons des stratégies de fallback de plus en plus flexibles :
|
||||
|
||||
```python
|
||||
# Si ça ne fonctionne pas, essayer avec une approche encore plus flexible
|
||||
if not context_match:
|
||||
# Extraire les mots significatifs du contexte (ignorer les mots très courts)
|
||||
words = [word for word in re.findall(r'\b\w+\b', context) if len(word) > 3]
|
||||
if words:
|
||||
# Créer un motif qui recherche une phrase contenant ces mots dans l'ordre
|
||||
words_pattern = r'.*'.join([re.escape(word) for word in words])
|
||||
context_match = re.search(words_pattern, chapter_content)
|
||||
|
||||
# Si toujours pas de correspondance, essayer de trouver juste l'erreur et son contexte immédiat
|
||||
if not context_match and error_text:
|
||||
# Chercher l'erreur avec quelques mots avant et après
|
||||
error_vicinity = r'[^\n]{0,30}' + re.escape(error_text) + r'[^\n]{0,30}'
|
||||
context_match = re.search(error_vicinity, chapter_content)
|
||||
```
|
||||
|
||||
### 3. Amélioration du remplacement de texte
|
||||
|
||||
Une fois le contexte trouvé, nous avons également amélioré la logique de remplacement pour gérer différents scénarios :
|
||||
|
||||
```python
|
||||
# Déterminer quelle stratégie de remplacement utiliser
|
||||
if matched_text == context:
|
||||
# Si le texte correspond exactement au contexte, utiliser la méthode simple
|
||||
corrected_context = context.replace(error_text, correction)
|
||||
modified_chapter = chapter_content.replace(matched_text, corrected_context)
|
||||
else:
|
||||
# Si nous avons utilisé une correspondance flexible, nous devons être plus précis
|
||||
# Trouver la position de l'erreur dans le contexte original
|
||||
error_pos = context.find(error_text)
|
||||
if error_pos >= 0:
|
||||
# Essayer de trouver l'erreur exacte dans le texte trouvé
|
||||
error_in_match = re.search(re.escape(error_text), matched_text)
|
||||
if error_in_match:
|
||||
# Remplacer directement l'erreur dans le texte trouvé
|
||||
start, end = error_in_match.span()
|
||||
corrected_matched_text = matched_text[:start] + correction + matched_text[end:]
|
||||
modified_chapter = chapter_content.replace(matched_text, corrected_matched_text)
|
||||
else:
|
||||
# Fallback: remplacer tout le texte trouvé
|
||||
corrected_context = context.replace(error_text, correction)
|
||||
modified_chapter = chapter_content.replace(matched_text, corrected_context)
|
||||
```
|
||||
|
||||
### 4. Ajout d'informations de débogage
|
||||
|
||||
Nous avons ajouté des logs détaillés pour faciliter le diagnostic des problèmes :
|
||||
|
||||
```python
|
||||
# Ajouter des logs pour le débogage
|
||||
print(f"Correction appliquée: '{error_text}' -> '{correction}'")
|
||||
print(f"Contexte trouvé: '{matched_text}'")
|
||||
print(f"Contexte après correction: '{corrected_context if 'corrected_context' in locals() else 'N/A'}'")
|
||||
|
||||
# Si le chapitre n'a pas été modifié, c'est qu'il y a un problème
|
||||
if modified_chapter == chapter_content:
|
||||
print("AVERTISSEMENT: Le chapitre n'a pas été modifié après la correction!")
|
||||
```
|
||||
|
||||
En cas d'échec, l'API retourne également des informations de débogage détaillées :
|
||||
|
||||
```python
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': 'Contexte non trouvé dans le chapitre',
|
||||
'debug_info': {
|
||||
'context': context,
|
||||
'error_text': error_text,
|
||||
'correction': correction,
|
||||
'chapter_excerpt': chapter_content[:200] # Premiers 200 caractères du chapitre
|
||||
}
|
||||
}), 404
|
||||
```
|
||||
|
||||
## Résultat
|
||||
|
||||
Avec ces modifications, l'API peut maintenant trouver et corriger correctement les erreurs grammaticales, même lorsque le contexte exact ne correspond pas parfaitement au texte du chapitre. Le test avec le payload spécifique mentionné dans la description du problème fonctionne désormais correctement.
|
||||
|
||||
## Améliorations futures possibles
|
||||
|
||||
1. **Mise en cache des correspondances** : Pour améliorer les performances, on pourrait mettre en cache les correspondances entre les contextes d'erreur et les positions dans le fichier.
|
||||
|
||||
2. **Interface utilisateur pour les erreurs** : Ajouter une interface utilisateur pour afficher les erreurs de correspondance et permettre à l'utilisateur de sélectionner manuellement le texte à corriger.
|
||||
|
||||
3. **Normalisation des rapports** : Modifier le processus de génération des rapports d'erreurs pour stocker plus d'informations sur le contexte exact, comme les positions des erreurs dans le fichier.
|
|
@ -15,8 +15,8 @@ import re
|
|||
import os
|
||||
import csv
|
||||
import argparse
|
||||
from spellchecker import SpellChecker
|
||||
import language_tool_python
|
||||
from spellchecker import SpellChecker
|
||||
|
||||
# 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.')
|
||||
|
|
|
@ -6,6 +6,12 @@ Ce script:
|
|||
1. Fournit des endpoints pour appliquer des corrections au fichier livre.org
|
||||
2. Permet d'ajouter des mots au dictionnaire personnalisé
|
||||
3. Permet de marquer des erreurs comme "à ne pas traiter" dans le CSV
|
||||
|
||||
Modifications (2025-08-30):
|
||||
- Amélioration de la recherche des chapitres dans le fichier livre.org
|
||||
- Le motif de recherche est maintenant plus flexible pour gérer les chapitres avec des tags ou du texte supplémentaire
|
||||
- Ajout de logs de débogage pour aider à diagnostiquer les problèmes de correspondance de chapitres
|
||||
- Amélioration des fonctions load_spelling_errors et load_grammar_errors pour utiliser une correspondance flexible
|
||||
"""
|
||||
|
||||
import os
|
||||
|
@ -59,11 +65,28 @@ def apply_correction(data):
|
|||
content = file.read()
|
||||
|
||||
# Trouver la section du chapitre
|
||||
chapter_pattern = r'^\*\* ' + re.escape(chapter) + r'$(.*?)(?=^\*\* |\Z)'
|
||||
# Motif plus flexible qui permet des textes supplémentaires après le nom du chapitre
|
||||
chapter_pattern = r'^\*\* ' + re.escape(chapter) + r'(?:\s+.*?)?$(.*?)(?=^\*\* |\Z)'
|
||||
chapter_match = re.search(chapter_pattern, content, re.MULTILINE | re.DOTALL)
|
||||
|
||||
# Si le motif flexible ne trouve pas de correspondance, essayer avec le motif original
|
||||
if not chapter_match:
|
||||
return jsonify({'success': False, 'message': f'Chapitre "{chapter}" non trouvé'}), 404
|
||||
# Ajouter des logs pour le débogage
|
||||
print(f"Recherche du chapitre: '{chapter}'")
|
||||
print(f"Motif utilisé: {chapter_pattern}")
|
||||
|
||||
# Lister tous les titres de chapitres dans le fichier pour le débogage
|
||||
all_chapters = re.findall(r'^\*\* (.*?)$', content, re.MULTILINE)
|
||||
print(f"Chapitres trouvés dans le fichier: {all_chapters}")
|
||||
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': f'Chapitre "{chapter}" non trouvé',
|
||||
'debug_info': {
|
||||
'pattern_used': chapter_pattern,
|
||||
'chapters_in_file': all_chapters
|
||||
}
|
||||
}), 404
|
||||
|
||||
chapter_content = chapter_match.group(1)
|
||||
chapter_start = chapter_match.start()
|
||||
|
@ -96,17 +119,91 @@ def apply_correction(data):
|
|||
context = error['context']
|
||||
|
||||
# Trouver le contexte dans le chapitre
|
||||
context_pattern = re.escape(context.replace(error_text, error_text))
|
||||
context_match = re.search(context_pattern, chapter_content)
|
||||
# Créer un motif plus flexible pour la recherche de contexte
|
||||
# 1. Échapper les caractères spéciaux dans le contexte
|
||||
escaped_context = re.escape(context)
|
||||
# 2. Remplacer les espaces échappées par un motif qui accepte plusieurs espaces
|
||||
flexible_context = escaped_context.replace('\\ ', '\\s+')
|
||||
# 3. Créer un motif qui permet des variations au début et à la fin du contexte
|
||||
flexible_pattern = flexible_context
|
||||
|
||||
# Essayer d'abord avec le motif flexible
|
||||
context_match = re.search(flexible_pattern, chapter_content)
|
||||
|
||||
# Si ça ne fonctionne pas, essayer avec une approche encore plus flexible
|
||||
if not context_match:
|
||||
# Extraire les mots significatifs du contexte (ignorer les mots très courts)
|
||||
words = [word for word in re.findall(r'\b\w+\b', context) if len(word) > 3]
|
||||
if words:
|
||||
# Créer un motif qui recherche une phrase contenant ces mots dans l'ordre
|
||||
words_pattern = r'.*'.join([re.escape(word) for word in words])
|
||||
context_match = re.search(words_pattern, chapter_content)
|
||||
|
||||
# Si toujours pas de correspondance, essayer de trouver juste l'erreur et son contexte immédiat
|
||||
if not context_match and error_text:
|
||||
# Chercher l'erreur avec quelques mots avant et après
|
||||
error_vicinity = r'[^\n]{0,30}' + re.escape(error_text) + r'[^\n]{0,30}'
|
||||
context_match = re.search(error_vicinity, chapter_content)
|
||||
|
||||
if not context_match:
|
||||
return jsonify({'success': False, 'message': 'Contexte non trouvé dans le chapitre'}), 404
|
||||
# Ajouter des logs de débogage
|
||||
print(f"Contexte non trouvé: '{context}'")
|
||||
print(f"Motif flexible utilisé: {flexible_pattern}")
|
||||
print(f"Erreur à corriger: '{error_text}'")
|
||||
print(f"Correction à appliquer: '{correction}'")
|
||||
print(f"Extrait du chapitre (100 premiers caractères): '{chapter_content[:100]}'")
|
||||
|
||||
# Remplacer l'erreur dans le contexte
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'message': 'Contexte non trouvé dans le chapitre',
|
||||
'debug_info': {
|
||||
'context': context,
|
||||
'error_text': error_text,
|
||||
'correction': correction,
|
||||
'chapter_excerpt': chapter_content[:200] # Premiers 200 caractères du chapitre
|
||||
}
|
||||
}), 404
|
||||
|
||||
# Remplacer l'erreur dans le contexte trouvé
|
||||
matched_text = context_match.group(0)
|
||||
|
||||
# Déterminer quelle stratégie de remplacement utiliser
|
||||
if matched_text == context:
|
||||
# Si le texte correspond exactement au contexte, utiliser la méthode simple
|
||||
corrected_context = context.replace(error_text, correction)
|
||||
modified_chapter = chapter_content.replace(matched_text, corrected_context)
|
||||
else:
|
||||
# Si nous avons utilisé une correspondance flexible, nous devons être plus précis
|
||||
# Trouver la position de l'erreur dans le contexte original
|
||||
error_pos = context.find(error_text)
|
||||
if error_pos >= 0:
|
||||
# Calculer les positions relatives dans le texte trouvé
|
||||
error_length = len(error_text)
|
||||
|
||||
# Remplacer le contexte dans le chapitre
|
||||
modified_chapter = chapter_content.replace(context, corrected_context)
|
||||
# Essayer de trouver l'erreur exacte dans le texte trouvé
|
||||
error_in_match = re.search(re.escape(error_text), matched_text)
|
||||
if error_in_match:
|
||||
# Remplacer directement l'erreur dans le texte trouvé
|
||||
start, end = error_in_match.span()
|
||||
corrected_matched_text = matched_text[:start] + correction + matched_text[end:]
|
||||
modified_chapter = chapter_content.replace(matched_text, corrected_matched_text)
|
||||
else:
|
||||
# Fallback: remplacer tout le texte trouvé
|
||||
corrected_context = context.replace(error_text, correction)
|
||||
modified_chapter = chapter_content.replace(matched_text, corrected_context)
|
||||
else:
|
||||
# Si nous ne pouvons pas localiser l'erreur, utiliser une approche plus simple
|
||||
corrected_context = context.replace(error_text, correction)
|
||||
modified_chapter = chapter_content.replace(matched_text, corrected_context)
|
||||
|
||||
# Ajouter des logs pour le débogage
|
||||
print(f"Correction appliquée: '{error_text}' -> '{correction}'")
|
||||
print(f"Contexte trouvé: '{matched_text}'")
|
||||
print(f"Contexte après correction: '{corrected_context if 'corrected_context' in locals() else 'N/A'}'")
|
||||
|
||||
# Si le chapitre n'a pas été modifié, c'est qu'il y a un problème
|
||||
if modified_chapter == chapter_content:
|
||||
print("AVERTISSEMENT: Le chapitre n'a pas été modifié après la correction!")
|
||||
|
||||
# Mettre à jour le contenu complet
|
||||
new_content = content[:chapter_start] + '** ' + chapter + modified_chapter + content[chapter_end:]
|
||||
|
@ -214,9 +311,20 @@ def load_spelling_errors(chapter):
|
|||
|
||||
errors_by_chapter = parse_error_report('rapport_orthographe_grammaire.md')
|
||||
|
||||
# Recherche exacte d'abord
|
||||
if chapter in errors_by_chapter:
|
||||
return errors_by_chapter[chapter]['spelling']
|
||||
|
||||
# Si pas trouvé, essayer une recherche plus flexible
|
||||
# Chercher un chapitre qui commence par le même nom (sans les tags ou autres textes)
|
||||
for chap_name in errors_by_chapter.keys():
|
||||
# Vérifier si le chapitre commence par le nom recherché
|
||||
if chap_name.startswith(chapter) or chapter.startswith(chap_name):
|
||||
print(f"Correspondance flexible trouvée: '{chapter}' -> '{chap_name}'")
|
||||
return errors_by_chapter[chap_name]['spelling']
|
||||
|
||||
print(f"Aucune erreur d'orthographe trouvée pour le chapitre: '{chapter}'")
|
||||
print(f"Chapitres disponibles: {list(errors_by_chapter.keys())}")
|
||||
return []
|
||||
|
||||
def load_grammar_errors(chapter):
|
||||
|
@ -230,9 +338,20 @@ def load_grammar_errors(chapter):
|
|||
|
||||
errors_by_chapter = parse_error_report('rapport_orthographe_grammaire.md')
|
||||
|
||||
# Recherche exacte d'abord
|
||||
if chapter in errors_by_chapter:
|
||||
return errors_by_chapter[chapter]['grammar']
|
||||
|
||||
# Si pas trouvé, essayer une recherche plus flexible
|
||||
# Chercher un chapitre qui commence par le même nom (sans les tags ou autres textes)
|
||||
for chap_name in errors_by_chapter.keys():
|
||||
# Vérifier si le chapitre commence par le nom recherché
|
||||
if chap_name.startswith(chapter) or chapter.startswith(chap_name):
|
||||
print(f"Correspondance flexible trouvée: '{chapter}' -> '{chap_name}'")
|
||||
return errors_by_chapter[chap_name]['grammar']
|
||||
|
||||
print(f"Aucune erreur grammaticale trouvée pour le chapitre: '{chapter}'")
|
||||
print(f"Chapitres disponibles: {list(errors_by_chapter.keys())}")
|
||||
return []
|
||||
|
||||
# Pour tester l'API indépendamment
|
||||
|
|
|
@ -6,3 +6,6 @@
|
|||
eryndor
|
||||
|
||||
# Termes spécifiques
|
||||
chuck
|
||||
bobette
|
||||
bleh
|
|
@ -33,7 +33,7 @@ Allez hein zou zou
|
|||
Là non plus pas de titre à afficher
|
||||
-------------
|
||||
|
||||
** Chapitre 1 : title:
|
||||
** Chapitre 1
|
||||
|
||||
Celui là on doit le voir : chapitre 1 au dessus ici.
|
||||
Dans un monde lointain, il y avait une île mystérieuse où les arbres avaient des feuilles qui brillaient comme des étoiles. Un jeune aventurier nommé Eryndor y arriva un jour, attiré par les légendes de l'île. Il découvrit un temple caché où les dieux anciens avaient laissé des secrets et des pouvoirs magiques.
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
|
||||
### Erreurs d'orthographe
|
||||
|
||||
- **eeeeeeeeeeeeeeeeeeeee**: Aucune suggestion
|
||||
- **eeeeeeeeeeeeeeeeee**: Aucune suggestion
|
||||
- **zou**: cou, mou, fou, zoo, ou
|
||||
- **zou**: fou, sou, pou, zoo, cou
|
||||
- **eeeeeeeeeeeeeeeeeeeee**: Aucune suggestion
|
||||
|
||||
### Erreurs grammaticales
|
||||
|
||||
|
@ -41,9 +41,8 @@ Aucune erreur grammaticale détectée.
|
|||
|
||||
### Erreurs d'orthographe
|
||||
|
||||
- **blah**: flash, ah, boa, lac, blet
|
||||
- **blah**: blasé, bac, beau, boa, la
|
||||
- **bleh**: bleu, blet
|
||||
- **eryndor**: Aucune suggestion
|
||||
|
||||
### Erreurs grammaticales
|
||||
|
||||
|
@ -101,7 +100,7 @@ Aucune erreur grammaticale détectée.
|
|||
|
||||
### Erreurs d'orthographe
|
||||
|
||||
- **bobette**: burette, tomette, omette, belette, molette
|
||||
- **bobette**: brouette, blette, couette, tomette, bette
|
||||
|
||||
### Erreurs grammaticales
|
||||
|
||||
|
@ -115,5 +114,5 @@ Aucune erreur grammaticale détectée.
|
|||
## Résumé
|
||||
|
||||
- **Nombre total de chapitres analysés**: 5
|
||||
- **Nombre total d'erreurs d'orthographe**: 8
|
||||
- **Nombre total d'erreurs d'orthographe**: 7
|
||||
- **Nombre total d'erreurs grammaticales**: 13
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
flask==3.0.2
|
||||
matplotlib>=3.8.0
|
||||
pandas>=2.0.0
|
||||
numpy>=1.21.0,<2.0.0
|
||||
scipy>=1.11.0
|
||||
argparse>=1.4.0
|
||||
pyspellchecker>=0.7.2
|
||||
language-tool-python>=2.7.1
|
||||
wordcloud>=1.9.2
|
||||
Flask==3.1.2
|
||||
language_tool_python==2.9.4
|
||||
matplotlib==3.10.6
|
||||
numpy==2.3.2
|
||||
pandas==2.3.2
|
||||
scipy==1.16.1
|
||||
wordcloud==1.9.4
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue