#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Script pour générer un README.md complet en utilisant les métadonnées des lettres de Jules Berton. Auteur: Assistant IA Date: Septembre 2025 """ import json import os from pathlib import Path from collections import defaultdict, Counter from datetime import datetime def load_metadata(json_path): """ Charge les métadonnées depuis le fichier JSON. Args: json_path (str): Chemin vers le fichier JSON Returns: list: Liste des métadonnées des lettres """ try: with open(json_path, 'r', encoding='utf-8') as f: return json.load(f) except FileNotFoundError: print(f"❌ Erreur: Le fichier '{json_path}' n'a pas été trouvé.") return [] except Exception as e: print(f"❌ Erreur lors de la lecture du fichier JSON: {e}") return [] def group_letters_by_date(metadata): """ Groupe les lettres par date (chaque ligne du Excel représente une page). Args: metadata (list): Liste des métadonnées Returns: dict: Dictionnaire groupé par date """ letters_by_date = defaultdict(list) for entry in metadata: date = entry.get('Date') if date: letters_by_date[date].append(entry) return dict(letters_by_date) def get_letter_info(date_entries): """ Extrait les informations principales d'une lettre à partir de ses pages. Args: date_entries (list): Liste des entrées pour une même date Returns: dict: Informations consolidées de la lettre """ # Prendre les informations de la première page (elles sont identiques pour toutes les pages) first_entry = date_entries[0] return { 'date': first_entry.get('Date'), 'lieu_epoque': first_entry.get('Lieu d\'époque'), 'pays_epoque': first_entry.get('Pays d\'époque'), 'bateau': first_entry.get('Bateau'), 'lieu_actuel': first_entry.get('Lieu actuel'), 'pays_actuel': first_entry.get('Pays actuel'), 'has_transcription': first_entry.get('has_transcription', False), 'has_images': first_entry.get('has_images', False), 'image_count': len(date_entries), # Nombre de pages 'pages': [entry.get('Page') for entry in date_entries] } def generate_statistics(letters_data): """ Génère des statistiques sur la collection. Args: letters_data (dict): Données des lettres groupées par date Returns: dict: Statistiques de la collection """ stats = { 'total_letters': len(letters_data), 'total_pages': sum(info['image_count'] for info in letters_data.values()), 'with_transcription': sum(1 for info in letters_data.values() if info['has_transcription']), 'with_images': sum(1 for info in letters_data.values() if info['has_images']), 'years': defaultdict(int), 'locations': Counter(), 'countries': Counter(), 'boats': Counter() } for date, info in letters_data.items(): # Statistiques par année year = date[:4] stats['years'][year] += 1 # Lieux et pays if info['lieu_epoque']: stats['locations'][info['lieu_epoque']] += 1 if info['pays_epoque']: stats['countries'][info['pays_epoque']] += 1 if info['bateau']: stats['boats'][info['bateau']] += 1 return stats def format_location_info(info): """ Formate les informations de lieu pour l'affichage. Args: info (dict): Informations de la lettre Returns: str: Lieu formaté """ lieu_parts = [] if info['lieu_epoque']: lieu_parts.append(info['lieu_epoque']) if info['pays_epoque'] and info['pays_epoque'] != info['lieu_epoque']: lieu_parts.append(f"({info['pays_epoque']})") return ", ".join(lieu_parts) if lieu_parts else "-" def format_current_location(info): """ Formate les informations de lieu actuel. Args: info (dict): Informations de la lettre Returns: str: Lieu actuel formaté """ if info['lieu_actuel']: if info['pays_actuel'] and info['pays_actuel'] != info['lieu_actuel']: return f"{info['lieu_actuel']} ({info['pays_actuel']})" return info['lieu_actuel'] return "-" def generate_image_links(info): """ Génère les liens vers les images. Args: info (dict): Informations de la lettre Returns: str: Liens formatés """ if not info['has_images']: return "❌" links = [] for i, page in enumerate(info['pages']): # Convertir en string et nettoyer page_str = str(page) if page else "" if page_str.endswith('.jpg'): page_letter = page_str.replace('.jpg', '') links.append(f"[{page_letter}](lettres_scannees/{info['date']}%20{page_str})") else: # Si ce n'est pas un nom de fichier, ignorer continue return ", ".join(links) if links else "❌" def generate_readme_content(letters_data, stats): """ Génère le contenu complet du README.md. Args: letters_data (dict): Données des lettres stats (dict): Statistiques Returns: str: Contenu du README """ content = [] # En-tête content.append("# Correspondance de Jules Berton") content.append("") content.append("## Description du projet") content.append("") content.append("Cette collection présente la correspondance de Jules Berton et de sa famille, ") content.append(f"comprenant **{stats['total_letters']} lettres** datées de 1875 à 1895, ") content.append(f"représentant un total de **{stats['total_pages']} pages**.") content.append("") content.append("La collection comprend :") content.append("- **Images scannées** : Les pages originales des lettres manuscrites (dossier `lettres_scannees/`)") content.append("- **Transcriptions** : Les transcriptions complètes des lettres en format Markdown (dossier `transcriptions/`)") content.append("- **Métadonnées** : Informations détaillées sur les dates, lieux et contexte (fichier Excel)") content.append("") # Statistiques content.append("## Statistiques de la collection") content.append("") content.append(f"- **Total des lettres** : {stats['total_letters']}") content.append(f"- **Total des pages** : {stats['total_pages']}") content.append(f"- **Lettres avec transcription** : {stats['with_transcription']} ({stats['with_transcription']/stats['total_letters']*100:.1f}%)") content.append(f"- **Lettres avec images** : {stats['with_images']} ({stats['with_images']/stats['total_letters']*100:.1f}%)") content.append("") # Répartition par année content.append("### Répartition par année") content.append("") for year in sorted(stats['years'].keys()): count = stats['years'][year] content.append(f"- **{year}** : {count} lettres") content.append("") # Lieux principaux if stats['locations']: content.append("### Lieux principaux mentionnés") content.append("") for lieu, count in stats['locations'].most_common(10): content.append(f"- **{lieu}** : {count} lettres") content.append("") # Navires if stats['boats']: content.append("### Navires mentionnés") content.append("") for bateau, count in stats['boats'].most_common(): content.append(f"- **{bateau}** : {count} lettres") content.append("") # Structure des fichiers content.append("## Organisation des données") content.append("") content.append("### Structure des fichiers") content.append("") content.append("```") content.append("├── Jules Berton - lettres, dates et lieux.xlsx # Métadonnées détaillées") content.append("├── lettres_scannees/ # Images originales") content.append("│ ├── YYYY-MM-DD a.jpg # Page 1 de la lettre") content.append("│ ├── YYYY-MM-DD b.jpg # Page 2 de la lettre") content.append("│ └── ...") content.append("├── transcriptions/ # Transcriptions textuelles") content.append("│ ├── YYYY-MM-DD.md # Transcription complète") content.append("│ └── ...") content.append("└── README.md # Ce fichier") content.append("```") content.append("") # Catalogue des lettres par année content.append("## Catalogue des lettres") content.append("") # Grouper par année letters_by_year = defaultdict(list) for date, info in sorted(letters_data.items()): year = date[:4] letters_by_year[year].append((date, info)) for year in sorted(letters_by_year.keys()): content.append(f"### Année {year}") content.append("") content.append("| Date | Lieu d'époque | Lieu actuel | Bateau | Transcription | Images | Pages |") content.append("|------|---------------|-------------|---------|---------------|---------|-------|") for date, info in letters_by_year[year]: transcription_link = f"[📝](transcriptions/{date}.md)" if info['has_transcription'] else "❌" images_link = generate_image_links(info) lieu_epoque = format_location_info(info) lieu_actuel = format_current_location(info) bateau = info['bateau'] if info['bateau'] else "-" content.append(f"| {date} | {lieu_epoque} | {lieu_actuel} | {bateau} | {transcription_link} | {images_link} | {info['image_count']} |") content.append("") # Informations sur les personnages content.append("## Personnages principaux") content.append("") content.append("- **Jules Berton** - Officier de marine, auteur principal des lettres") content.append("- **Marie** - Sœur de Jules, destinataire fréquente") content.append("- **Henriette** - Sœur") content.append("- **Camille** - Beau-frère") content.append("- **M. de Brazza** - Administrateur colonial mentionné") content.append("") # Thèmes content.append("## Thèmes récurrents") content.append("") content.append("- **Correspondance familiale** - Échanges entre frères et sœurs") content.append("- **Carrière navale** - Vie d'officier de marine, embarquements, navigations") content.append("- **Missions coloniales** - Service au Gabon, en Afrique équatoriale") content.append("- **Deuil familial** - Mort du père en 1886") content.append("- **Vie sociale** - Relations avec les familles et amis") content.append("") # Notes techniques content.append("## Notes techniques") content.append("") content.append("- Les transcriptions respectent l'orthographe et la ponctuation originales") content.append("- Les lettres sont organisées par ordre chronologique") content.append("- Les métadonnées incluent les lieux d'époque et actuels quand disponibles") content.append("- Les images sont numérisées en haute résolution au format JPG") content.append("- Certaines lettres peuvent avoir plusieurs versions (suffixe -2)") content.append("") content.append("---") content.append("") content.append(f"*Dernière mise à jour : {datetime.now().strftime('%d %B %Y')}*") content.append(f"*Généré automatiquement à partir des métadonnées Excel*") return "\n".join(content) def main(): """Fonction principale.""" base_path = Path(__file__).parent json_path = base_path / "lettres_metadata.json" readme_path = base_path / "README.md" print("📚 GÉNÉRATION DU README.MD AVEC MÉTADONNÉES") print("=" * 50) # Charger les métadonnées metadata = load_metadata(json_path) if not metadata: return print(f"✓ {len(metadata)} entrées chargées depuis le JSON") # Grouper par date letters_by_date = group_letters_by_date(metadata) # Consolider les informations par lettre letters_data = {} for date, entries in letters_by_date.items(): letters_data[date] = get_letter_info(entries) print(f"✓ {len(letters_data)} lettres consolidées") # Générer les statistiques stats = generate_statistics(letters_data) print(f"✓ Statistiques générées") print(f" - {stats['total_letters']} lettres") print(f" - {stats['total_pages']} pages") print(f" - {len(stats['years'])} années couvertes") print(f" - {len(stats['locations'])} lieux différents") print(f" - {len(stats['boats'])} navires mentionnés") # Générer le contenu du README readme_content = generate_readme_content(letters_data, stats) # Écrire le fichier try: with open(readme_path, 'w', encoding='utf-8') as f: f.write(readme_content) print(f"✓ README.md généré avec succès : {readme_path}") except Exception as e: print(f"❌ Erreur lors de l'écriture : {e}") if __name__ == "__main__": main()