Initial commit
This commit is contained in:
346
scripts/load_referentiels.py
Executable file
346
scripts/load_referentiels.py
Executable file
@@ -0,0 +1,346 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Script pour charger et indexer tous les référentiels médicaux.
|
||||
|
||||
Ce script :
|
||||
1. Charge et indexe CIM-10 2026 depuis le PDF
|
||||
2. Convertit/vérifie CCAM V81 → 2025
|
||||
3. Extrait le Guide Méthodologique MCO 2026
|
||||
4. Utilise le GPU pour l'indexation FAISS
|
||||
|
||||
Usage:
|
||||
python scripts/load_referentiels.py
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import List, Dict
|
||||
|
||||
# Ajouter le répertoire parent au path
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
try:
|
||||
import pypdf
|
||||
PDF_SUPPORT = True
|
||||
except ImportError:
|
||||
PDF_SUPPORT = False
|
||||
print("⚠️ pypdf non installé. Installez-le avec: pip install pypdf")
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
import pandas as pd
|
||||
EXCEL_SUPPORT = True
|
||||
except ImportError:
|
||||
EXCEL_SUPPORT = False
|
||||
print("⚠️ pandas non installé. Installez-le avec: pip install pandas openpyxl")
|
||||
sys.exit(1)
|
||||
|
||||
from pipeline_mco_pmsi.rag.referentiels_manager import ReferentielsManager
|
||||
|
||||
# Configuration du logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def extract_text_from_pdf(pdf_path: Path) -> str:
|
||||
"""Extrait le texte d'un fichier PDF."""
|
||||
logger.info(f"📄 Extraction du PDF: {pdf_path.name}")
|
||||
|
||||
text_parts = []
|
||||
try:
|
||||
with open(pdf_path, 'rb') as f:
|
||||
reader = pypdf.PdfReader(f)
|
||||
|
||||
if reader.is_encrypted:
|
||||
try:
|
||||
reader.decrypt('')
|
||||
except:
|
||||
raise RuntimeError(f"PDF protégé par mot de passe: {pdf_path.name}")
|
||||
|
||||
total_pages = len(reader.pages)
|
||||
logger.info(f" {total_pages} pages à traiter...")
|
||||
|
||||
for i, page in enumerate(reader.pages, 1):
|
||||
if i % 50 == 0:
|
||||
logger.info(f" Progression: {i}/{total_pages} pages")
|
||||
text = page.extract_text()
|
||||
if text:
|
||||
text_parts.append(text)
|
||||
|
||||
full_text = '\n\n'.join(text_parts)
|
||||
logger.info(f"✅ Extraction terminée: {len(full_text)} caractères")
|
||||
return full_text
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Erreur extraction PDF: {e}")
|
||||
raise
|
||||
|
||||
|
||||
def load_cim10(data_dir: Path, manager: ReferentielsManager) -> bool:
|
||||
"""Charge et indexe le référentiel CIM-10 2026."""
|
||||
logger.info("\n" + "="*60)
|
||||
logger.info("🏥 CHARGEMENT CIM-10 2026")
|
||||
logger.info("="*60)
|
||||
|
||||
pdf_path = Path("cim-10-fr_2026_a_usage_pmsi_version_provisoire_111225.pdf")
|
||||
|
||||
if not pdf_path.exists():
|
||||
logger.error(f"❌ Fichier CIM-10 introuvable: {pdf_path}")
|
||||
return False
|
||||
|
||||
try:
|
||||
# Extraire le texte du PDF
|
||||
text = extract_text_from_pdf(pdf_path)
|
||||
|
||||
# Sauvegarder le texte brut
|
||||
text_file = data_dir / "cim10_2026_text.txt"
|
||||
logger.info(f"💾 Sauvegarde du texte: {text_file}")
|
||||
with open(text_file, 'w', encoding='utf-8') as f:
|
||||
f.write(text)
|
||||
|
||||
# Découper en chunks et indexer
|
||||
logger.info("🔪 Découpage en chunks...")
|
||||
chunks = manager.chunk_cim10(text, "2026")
|
||||
logger.info(f" {len(chunks)} chunks créés")
|
||||
|
||||
# Construire l'index FAISS
|
||||
logger.info("🔍 Construction de l'index FAISS...")
|
||||
index = manager.build_index(chunks)
|
||||
logger.info(f" Index créé avec {index.dimension} dimensions")
|
||||
|
||||
# Sauvegarder les chunks
|
||||
chunks_file = data_dir / "cim10_2026_chunks.json"
|
||||
logger.info(f"💾 Sauvegarde des chunks: {chunks_file}")
|
||||
chunks_data = [chunk.model_dump() for chunk in chunks]
|
||||
with open(chunks_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(chunks_data, f, ensure_ascii=False, indent=2)
|
||||
|
||||
logger.info("✅ CIM-10 2026 chargé et indexé avec succès!")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Erreur chargement CIM-10: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
|
||||
def load_ccam(data_dir: Path, manager: ReferentielsManager) -> bool:
|
||||
"""Charge et indexe le référentiel CCAM 2025 (V81)."""
|
||||
logger.info("\n" + "="*60)
|
||||
logger.info("🔧 CHARGEMENT CCAM 2025 (V81)")
|
||||
logger.info("="*60)
|
||||
|
||||
# Vérifier si les fichiers V81 existent
|
||||
v81_chunks = data_dir / "ccam_V81_chunks.json"
|
||||
v81_text = data_dir / "ccam_V81_text.txt"
|
||||
|
||||
if v81_chunks.exists() and v81_text.exists():
|
||||
logger.info("📦 Fichiers CCAM V81 trouvés, conversion en version 2025...")
|
||||
|
||||
try:
|
||||
# Charger les chunks V81
|
||||
with open(v81_chunks, 'r', encoding='utf-8') as f:
|
||||
chunks_data = json.load(f)
|
||||
|
||||
logger.info(f" {len(chunks_data)} chunks trouvés")
|
||||
|
||||
# Convertir en version 2025
|
||||
logger.info("🔄 Conversion V81 → 2025...")
|
||||
for chunk in chunks_data:
|
||||
chunk['referentiel_version'] = '2025'
|
||||
chunk['chunk_id'] = chunk['chunk_id'].replace('V81', '2025')
|
||||
|
||||
# Sauvegarder avec le nouveau nom
|
||||
chunks_2025 = data_dir / "ccam_2025_chunks.json"
|
||||
with open(chunks_2025, 'w', encoding='utf-8') as f:
|
||||
json.dump(chunks_data, f, ensure_ascii=False, indent=2)
|
||||
|
||||
# Copier le fichier texte
|
||||
text_2025 = data_dir / "ccam_2025_text.txt"
|
||||
with open(v81_text, 'r', encoding='utf-8') as f:
|
||||
text_content = f.read()
|
||||
with open(text_2025, 'w', encoding='utf-8') as f:
|
||||
f.write(text_content)
|
||||
|
||||
# Réindexer avec FAISS
|
||||
logger.info("🔍 Construction de l'index FAISS...")
|
||||
|
||||
# Recréer les objets Chunk
|
||||
from pipeline_mco_pmsi.rag.referentiels_manager import Chunk
|
||||
chunks_objects = [Chunk(**chunk) for chunk in chunks_data]
|
||||
|
||||
# Construire l'index
|
||||
index = manager.build_index(chunks_objects)
|
||||
logger.info(f" Index créé avec {index.dimension} dimensions")
|
||||
|
||||
logger.info("✅ CCAM 2025 converti et indexé avec succès!")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Erreur conversion CCAM: {e}")
|
||||
logger.info(" Tentative de rechargement depuis le fichier Excel...")
|
||||
|
||||
# Si conversion échoue ou fichiers absents, charger depuis Excel
|
||||
excel_path = Path("CCAM_V81.xls")
|
||||
if not excel_path.exists():
|
||||
excel_path = data_dir / "CCAM_V81.xls"
|
||||
|
||||
if not excel_path.exists():
|
||||
logger.error(f"❌ Fichier CCAM introuvable: CCAM_V81.xls")
|
||||
return False
|
||||
|
||||
try:
|
||||
logger.info(f"📊 Lecture du fichier Excel: {excel_path.name}")
|
||||
|
||||
# Lire le fichier Excel
|
||||
df = pd.read_excel(excel_path)
|
||||
logger.info(f" {len(df)} lignes trouvées")
|
||||
|
||||
# Extraire le texte (adapter selon la structure du fichier)
|
||||
text_parts = []
|
||||
for _, row in df.iterrows():
|
||||
# Adapter selon les colonnes du fichier CCAM
|
||||
row_text = ' '.join(str(val) for val in row.values if pd.notna(val))
|
||||
text_parts.append(row_text)
|
||||
|
||||
text = '\n\n'.join(text_parts)
|
||||
|
||||
# Sauvegarder le texte
|
||||
text_file = data_dir / "ccam_2025_text.txt"
|
||||
logger.info(f"💾 Sauvegarde du texte: {text_file}")
|
||||
with open(text_file, 'w', encoding='utf-8') as f:
|
||||
f.write(text)
|
||||
|
||||
# Découper et indexer
|
||||
logger.info("🔪 Découpage en chunks...")
|
||||
chunks = manager.chunk_ccam(text, "2025")
|
||||
logger.info(f" {len(chunks)} chunks créés")
|
||||
|
||||
# Construire l'index FAISS
|
||||
logger.info("🔍 Construction de l'index FAISS...")
|
||||
index = manager.build_index(chunks)
|
||||
logger.info(f" Index créé avec {index.dimension} dimensions")
|
||||
|
||||
# Sauvegarder les chunks
|
||||
chunks_file = data_dir / "ccam_2025_chunks.json"
|
||||
logger.info(f"💾 Sauvegarde des chunks: {chunks_file}")
|
||||
chunks_data = [chunk.model_dump() for chunk in chunks]
|
||||
with open(chunks_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(chunks_data, f, ensure_ascii=False, indent=2)
|
||||
|
||||
logger.info("✅ CCAM 2025 chargé et indexé avec succès!")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Erreur chargement CCAM: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
|
||||
def load_guide_mco(data_dir: Path, manager: ReferentielsManager) -> bool:
|
||||
"""Charge et indexe le Guide Méthodologique MCO 2026."""
|
||||
logger.info("\n" + "="*60)
|
||||
logger.info("📚 CHARGEMENT GUIDE MÉTHODOLOGIQUE MCO 2026")
|
||||
logger.info("="*60)
|
||||
|
||||
pdf_path = Path("guide_methodo_mco_2026_version_provisoire.pdf")
|
||||
|
||||
if not pdf_path.exists():
|
||||
logger.warning(f"⚠️ Fichier Guide MCO introuvable: {pdf_path}")
|
||||
logger.info(" Le système fonctionnera sans le guide (optionnel)")
|
||||
return False
|
||||
|
||||
try:
|
||||
# Extraire le texte du PDF
|
||||
text = extract_text_from_pdf(pdf_path)
|
||||
|
||||
# Sauvegarder le texte brut
|
||||
text_file = data_dir / "guide_mco_2026_text.txt"
|
||||
logger.info(f"💾 Sauvegarde du texte: {text_file}")
|
||||
with open(text_file, 'w', encoding='utf-8') as f:
|
||||
f.write(text)
|
||||
|
||||
# Découper en chunks et indexer
|
||||
logger.info("🔪 Découpage en chunks...")
|
||||
chunks = manager.chunk_guide_mco(text, "2026")
|
||||
logger.info(f" {len(chunks)} chunks créés")
|
||||
|
||||
# Construire l'index FAISS
|
||||
logger.info("🔍 Construction de l'index FAISS...")
|
||||
index = manager.build_index(chunks)
|
||||
logger.info(f" Index créé avec {index.dimension} dimensions")
|
||||
|
||||
# Sauvegarder les chunks
|
||||
chunks_file = data_dir / "guide_mco_2026_chunks.json"
|
||||
logger.info(f"💾 Sauvegarde des chunks: {chunks_file}")
|
||||
chunks_data = [chunk.model_dump() for chunk in chunks]
|
||||
with open(chunks_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(chunks_data, f, ensure_ascii=False, indent=2)
|
||||
|
||||
logger.info("✅ Guide MCO 2026 chargé et indexé avec succès!")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Erreur chargement Guide MCO: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
"""Point d'entrée principal."""
|
||||
logger.info("\n" + "🚀 "*30)
|
||||
logger.info("CHARGEMENT DES RÉFÉRENTIELS MÉDICAUX")
|
||||
logger.info("🚀 "*30 + "\n")
|
||||
|
||||
# Créer le répertoire de données si nécessaire
|
||||
data_dir = Path("data/referentiels")
|
||||
data_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Initialiser le ReferentielsManager
|
||||
logger.info(f"📁 Répertoire de données: {data_dir.absolute()}")
|
||||
manager = ReferentielsManager(data_dir=data_dir)
|
||||
|
||||
# Charger les référentiels
|
||||
results = {
|
||||
"CIM-10 2026": load_cim10(data_dir, manager),
|
||||
"CCAM 2025": load_ccam(data_dir, manager),
|
||||
"Guide MCO 2026": load_guide_mco(data_dir, manager),
|
||||
}
|
||||
|
||||
# Résumé
|
||||
logger.info("\n" + "="*60)
|
||||
logger.info("📊 RÉSUMÉ DU CHARGEMENT")
|
||||
logger.info("="*60)
|
||||
|
||||
for name, success in results.items():
|
||||
status = "✅ OK" if success else "❌ ÉCHEC"
|
||||
logger.info(f" {name}: {status}")
|
||||
|
||||
# Vérifier les fichiers créés
|
||||
logger.info("\n📦 Fichiers créés:")
|
||||
for file in sorted(data_dir.glob("*_2025_*")) + sorted(data_dir.glob("*_2026_*")):
|
||||
size_mb = file.stat().st_size / (1024 * 1024)
|
||||
logger.info(f" {file.name} ({size_mb:.1f} MB)")
|
||||
|
||||
# Statut final
|
||||
all_success = all(results.values())
|
||||
if all_success:
|
||||
logger.info("\n🎉 Tous les référentiels ont été chargés avec succès!")
|
||||
logger.info(" Le système est prêt à traiter des séjours.")
|
||||
else:
|
||||
logger.warning("\n⚠️ Certains référentiels n'ont pas pu être chargés.")
|
||||
logger.info(" Le système fonctionnera avec les référentiels disponibles.")
|
||||
|
||||
return 0 if all_success else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user