- Implemented main.py to orchestrate the analysis of Jules Berton's letters collection. - Added read_excel_metadata.py to read and analyze the Excel file containing letter metadata. - Included functions for reading Excel files, analyzing structure, extracting letter information, and saving data to JSON. - Added error handling and user feedback for file operations and analysis steps. - Provided a summary of the analysis results and instructions for further usage.
254 lines
8.5 KiB
Python
254 lines
8.5 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
Script pour lire et analyser le fichier Excel contenant les métadonnées
|
||
des lettres de Jules Berton.
|
||
|
||
Auteur: Assistant IA
|
||
Date: Septembre 2025
|
||
"""
|
||
|
||
import pandas as pd
|
||
import os
|
||
import json
|
||
from pathlib import Path
|
||
from datetime import datetime
|
||
|
||
def read_excel_file(excel_path):
|
||
"""
|
||
Lit le fichier Excel et retourne un DataFrame avec les métadonnées des lettres.
|
||
|
||
Args:
|
||
excel_path (str): Chemin vers le fichier Excel
|
||
|
||
Returns:
|
||
pandas.DataFrame: DataFrame contenant les données du fichier Excel
|
||
"""
|
||
try:
|
||
# Essayer de lire avec différents moteurs
|
||
try:
|
||
df = pd.read_excel(excel_path, engine='openpyxl')
|
||
print(f"✓ Fichier Excel lu avec succès (moteur: openpyxl)")
|
||
except:
|
||
try:
|
||
df = pd.read_excel(excel_path, engine='xlrd')
|
||
print(f"✓ Fichier Excel lu avec succès (moteur: xlrd)")
|
||
except:
|
||
df = pd.read_excel(excel_path)
|
||
print(f"✓ Fichier Excel lu avec succès (moteur par défaut)")
|
||
|
||
print(f"📊 Dimensions du fichier: {df.shape[0]} lignes × {df.shape[1]} colonnes")
|
||
return df
|
||
|
||
except FileNotFoundError:
|
||
print(f"❌ Erreur: Le fichier '{excel_path}' n'a pas été trouvé.")
|
||
return None
|
||
except Exception as e:
|
||
print(f"❌ Erreur lors de la lecture du fichier Excel: {e}")
|
||
return None
|
||
|
||
def analyze_excel_structure(df):
|
||
"""
|
||
Analyse la structure du DataFrame et affiche des informations sur les colonnes.
|
||
|
||
Args:
|
||
df (pandas.DataFrame): DataFrame à analyser
|
||
"""
|
||
if df is None:
|
||
return
|
||
|
||
print("\n📋 STRUCTURE DU FICHIER EXCEL")
|
||
print("=" * 50)
|
||
|
||
# Informations générales
|
||
print(f"Nombre total de lignes: {len(df)}")
|
||
print(f"Nombre total de colonnes: {len(df.columns)}")
|
||
|
||
# Liste des colonnes
|
||
print("\n📝 COLONNES DISPONIBLES:")
|
||
for i, col in enumerate(df.columns, 1):
|
||
print(f"{i:2d}. {col}")
|
||
|
||
# Aperçu des premières lignes
|
||
print("\n👀 APERÇU DES PREMIÈRES LIGNES:")
|
||
print(df.head().to_string())
|
||
|
||
# Informations sur les types de données
|
||
print("\n🔢 TYPES DE DONNÉES:")
|
||
for col in df.columns:
|
||
print(f"{col}: {df[col].dtype}")
|
||
|
||
# Statistiques sur les valeurs manquantes
|
||
print("\n❓ VALEURS MANQUANTES:")
|
||
missing_data = df.isnull().sum()
|
||
for col in df.columns:
|
||
if missing_data[col] > 0:
|
||
percentage = (missing_data[col] / len(df)) * 100
|
||
print(f"{col}: {missing_data[col]} ({percentage:.1f}%)")
|
||
|
||
def extract_letter_info(df):
|
||
"""
|
||
Extrait et structure les informations sur chaque lettre.
|
||
|
||
Args:
|
||
df (pandas.DataFrame): DataFrame contenant les métadonnées
|
||
|
||
Returns:
|
||
list: Liste de dictionnaires contenant les informations de chaque lettre
|
||
"""
|
||
if df is None:
|
||
return []
|
||
|
||
letters_info = []
|
||
|
||
print("\n📮 EXTRACTION DES INFORMATIONS DES LETTRES")
|
||
print("=" * 50)
|
||
|
||
for index, row in df.iterrows():
|
||
letter_info = {}
|
||
|
||
# Parcourir toutes les colonnes et extraire les données
|
||
for col in df.columns:
|
||
value = row[col]
|
||
# Nettoyer les valeurs NaN
|
||
if pd.isna(value):
|
||
value = None
|
||
elif isinstance(value, str):
|
||
value = value.strip()
|
||
|
||
letter_info[col] = value
|
||
|
||
letter_info['index'] = index
|
||
letters_info.append(letter_info)
|
||
|
||
print(f"✓ {len(letters_info)} lettres extraites")
|
||
return letters_info
|
||
|
||
def save_to_json(data, output_path):
|
||
"""
|
||
Sauvegarde les données extraites en format JSON.
|
||
|
||
Args:
|
||
data (list): Données à sauvegarder
|
||
output_path (str): Chemin de sortie pour le fichier JSON
|
||
"""
|
||
try:
|
||
with open(output_path, 'w', encoding='utf-8') as f:
|
||
json.dump(data, f, ensure_ascii=False, indent=2, default=str)
|
||
print(f"✓ Données sauvegardées dans: {output_path}")
|
||
except Exception as e:
|
||
print(f"❌ Erreur lors de la sauvegarde: {e}")
|
||
|
||
def check_files_existence(letters_info, base_path):
|
||
"""
|
||
Vérifie l'existence des fichiers de transcription et d'images pour chaque lettre.
|
||
|
||
Args:
|
||
letters_info (list): Liste des informations des lettres
|
||
base_path (str): Chemin de base du projet
|
||
"""
|
||
print("\n🔍 VÉRIFICATION DE L'EXISTENCE DES FICHIERS")
|
||
print("=" * 50)
|
||
|
||
transcriptions_path = Path(base_path) / "transcriptions"
|
||
images_path = Path(base_path) / "lettres_scannees"
|
||
|
||
for letter in letters_info:
|
||
# Essayer d'identifier la date dans les données
|
||
date_found = None
|
||
|
||
# Chercher une colonne qui pourrait contenir la date
|
||
for key, value in letter.items():
|
||
if value and isinstance(value, (str, datetime)):
|
||
if isinstance(value, datetime):
|
||
date_found = value.strftime("%Y-%m-%d")
|
||
break
|
||
elif isinstance(value, str) and len(value) >= 8:
|
||
# Essayer de parser différents formats de date
|
||
try:
|
||
# Format YYYY-MM-DD
|
||
if '-' in value and len(value.split('-')) == 3:
|
||
parts = value.split('-')
|
||
if len(parts[0]) == 4:
|
||
date_found = value[:10] # Prendre les 10 premiers caractères
|
||
break
|
||
except:
|
||
continue
|
||
|
||
if date_found:
|
||
# Vérifier transcription
|
||
transcription_file = transcriptions_path / f"{date_found}.md"
|
||
has_transcription = transcription_file.exists()
|
||
|
||
# Vérifier images
|
||
image_pattern = f"{date_found} *.jpg"
|
||
image_files = list(images_path.glob(image_pattern))
|
||
has_images = len(image_files) > 0
|
||
|
||
letter['date_parsed'] = date_found
|
||
letter['has_transcription'] = has_transcription
|
||
letter['has_images'] = has_images
|
||
letter['image_count'] = len(image_files)
|
||
|
||
status = "📝" if has_transcription else "❌"
|
||
status += " 🖼️" if has_images else " ❌"
|
||
print(f"{date_found}: {status} (images: {len(image_files)})")
|
||
else:
|
||
letter['date_parsed'] = None
|
||
letter['has_transcription'] = False
|
||
letter['has_images'] = False
|
||
letter['image_count'] = 0
|
||
print(f"Ligne {letter['index']}: ❌ Date non identifiée")
|
||
|
||
def main():
|
||
"""Fonction principale du script."""
|
||
|
||
# Chemin du fichier Excel
|
||
excel_file = "Jules Berton - lettres, dates et lieux.xlsx"
|
||
base_path = Path(__file__).parent
|
||
excel_path = base_path / excel_file
|
||
|
||
print("🔍 LECTURE DES MÉTADONNÉES DES LETTRES DE JULES BERTON")
|
||
print("=" * 60)
|
||
print(f"📁 Répertoire de travail: {base_path}")
|
||
print(f"📊 Fichier Excel: {excel_file}")
|
||
|
||
# Vérifier que le fichier existe
|
||
if not excel_path.exists():
|
||
print(f"❌ Le fichier {excel_file} n'existe pas dans le répertoire courant.")
|
||
print(f" Chemin recherché: {excel_path}")
|
||
return
|
||
|
||
# Lire le fichier Excel
|
||
df = read_excel_file(excel_path)
|
||
if df is None:
|
||
return
|
||
|
||
# Analyser la structure
|
||
analyze_excel_structure(df)
|
||
|
||
# Extraire les informations des lettres
|
||
letters_info = extract_letter_info(df)
|
||
|
||
# Vérifier l'existence des fichiers
|
||
check_files_existence(letters_info, base_path)
|
||
|
||
# Sauvegarder en JSON
|
||
output_json = base_path / "lettres_metadata.json"
|
||
save_to_json(letters_info, output_json)
|
||
|
||
# Statistiques finales
|
||
print(f"\n📊 STATISTIQUES FINALES")
|
||
print("=" * 30)
|
||
total_letters = len(letters_info)
|
||
with_transcription = sum(1 for l in letters_info if l.get('has_transcription', False))
|
||
with_images = sum(1 for l in letters_info if l.get('has_images', False))
|
||
|
||
print(f"Total des lettres dans Excel: {total_letters}")
|
||
print(f"Lettres avec transcription: {with_transcription}")
|
||
print(f"Lettres avec images: {with_images}")
|
||
print(f"Lettres complètes (texte + images): {sum(1 for l in letters_info if l.get('has_transcription', False) and l.get('has_images', False))}")
|
||
|
||
if __name__ == "__main__":
|
||
main()
|