#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Script d'analyse des enchaînements d'intrigue dans le livre. Ce script: 1. Analyse le CSV des intrigues 2. Attribue une couleur à chaque ligne d'intrigue selon un gradient rouge-vert (rouge pour le début, vert pour la fin) 3. Réalise un graphique avec une seule ligne faite de segments colorés 4. Sauvegarde le graphique en PNG et SVG 5. Crée une page web interactive dans le dossier build """ import pandas as pd import matplotlib.pyplot as plt import matplotlib.colors as mcolors import numpy as np import os import json from matplotlib.colors import to_hex import shutil # Créer le dossier build s'il n'existe pas if not os.path.exists('build'): os.makedirs('build') # Fonction pour générer un gradient de couleurs du rouge au vert def generate_red_to_green_gradient(n): """Génère n couleurs dans un gradient allant du rouge au vert""" # Utiliser un gradient de rouge (début) à vert (fin) colors = [] for i in range(n): # Calculer la position dans le gradient (0 = rouge, 1 = vert) t = i / max(1, n - 1) # Rouge: diminue de 1 à 0 r = 1.0 - t # Vert: augmente de 0 à 1 g = t # Bleu: toujours à 0 b = 0.0 colors.append(np.array([r, g, b, 1.0])) return colors # Lire le fichier CSV des intrigues df = pd.read_csv('intrigues.csv') # Trier les intrigues par début df = df.sort_values(by=['Début', 'Fin']) # Identifier les intrigues principales et les sous-intrigues # Une sous-intrigue est contenue dans une autre intrigue si son début et sa fin # sont compris dans l'intervalle de l'intrigue parente def identify_subplots(df): """Identifie les relations parent-enfant entre les intrigues""" # Initialiser une colonne pour le parent df['Parent'] = None # Pour chaque intrigue, trouver son parent (s'il existe) for i, row in df.iterrows(): # Chercher parmi toutes les autres intrigues for j, potential_parent in df.iterrows(): if i != j: # Ne pas comparer avec soi-même # Si l'intrigue est contenue dans une autre if (row['Début'] >= potential_parent['Début'] and row['Fin'] <= potential_parent['Fin'] and # S'assurer que ce n'est pas exactement le même intervalle not (row['Début'] == potential_parent['Début'] and row['Fin'] == potential_parent['Fin'])): # Si on a déjà trouvé un parent, prendre le plus spécifique if df.at[i, 'Parent'] is not None: current_parent_idx = df.index[df['Intrigue'] == df.at[i, 'Parent']].tolist()[0] current_parent = df.iloc[current_parent_idx] # Le parent le plus spécifique est celui avec l'intervalle le plus court if ((potential_parent['Fin'] - potential_parent['Début']) < (current_parent['Fin'] - current_parent['Début'])): df.at[i, 'Parent'] = potential_parent['Intrigue'] else: df.at[i, 'Parent'] = potential_parent['Intrigue'] return df # Appliquer la fonction pour identifier les sous-intrigues df = identify_subplots(df) # Organiser les intrigues en groupes basés sur les relations parent-enfant def organize_plots_by_hierarchy(df): # Créer un dictionnaire pour stocker les groupes d'intrigues plot_groups = {} group_id = 0 # D'abord, identifier toutes les intrigues principales (sans parent) main_plots = df[df['Parent'].isna()]['Intrigue'].tolist() # Pour chaque intrigue principale, créer un groupe avec ses sous-intrigues for main_plot in main_plots: group = [main_plot] # Trouver toutes les sous-intrigues directes subplots = df[df['Parent'] == main_plot]['Intrigue'].tolist() group.extend(subplots) # Trouver les sous-intrigues de niveau 2 (sous-intrigues des sous-intrigues) for subplot in subplots: sub_subplots = df[df['Parent'] == subplot]['Intrigue'].tolist() group.extend(sub_subplots) plot_groups[group_id] = group group_id += 1 # Créer un dictionnaire qui associe chaque intrigue à son groupe plot_to_group = {} for group_id, plots in plot_groups.items(): for plot in plots: plot_to_group[plot] = group_id return plot_groups, plot_to_group # Déterminer les valeurs min et max pour les chapitres min_chapter = df['Début'].min() max_chapter = df['Fin'].max() chapter_range = max_chapter - min_chapter # Créer un dictionnaire de couleurs pour chaque intrigue color_dict = {} # Trier les intrigues par position dans l'histoire (début du chapitre) sorted_intrigues = df.sort_values(by='Début') # Générer un gradient de couleurs du rouge au vert gradient_colors = generate_red_to_green_gradient(len(df)) # Assigner les couleurs aux intrigues en fonction de leur position dans l'histoire for i, (_, row) in enumerate(sorted_intrigues.iterrows()): intrigue = row['Intrigue'] # Utiliser la position normalisée dans l'histoire pour déterminer la couleur position = (row['Début'] - min_chapter) / chapter_range if chapter_range > 0 else 0 # Assigner la couleur du gradient correspondant à la position color_index = min(int(position * (len(gradient_colors) - 1)), len(gradient_colors) - 1) color_dict[intrigue] = to_hex(gradient_colors[i]) # Créer un graphique avec une ligne unique composée de segments colorés plt.figure(figsize=(12, 4)) # Tracer chaque segment d'intrigue for i, row in df.iterrows(): plt.plot([row['Début'], row['Fin']], [1, 1], linewidth=10, color=color_dict[row['Intrigue']], solid_capstyle='butt', label=row['Intrigue']) # Ajouter des marqueurs pour les points de début et fin for i, row in df.iterrows(): plt.plot(row['Début'], 1, 'o', color='black', markersize=5) plt.plot(row['Fin'], 1, 'o', color='black', markersize=5) # Configurer les axes plt.yticks([]) # Cacher l'axe y plt.xlabel('Chapitres') plt.title('Enchaînement des intrigues') # Ajouter une légende unique pour chaque intrigue handles, labels = plt.gca().get_legend_handles_labels() by_label = dict(zip(labels, handles)) plt.legend(by_label.values(), by_label.keys(), loc='upper center', bbox_to_anchor=(0.5, -0.15), ncol=3) # Ajuster les marges plt.tight_layout() # Sauvegarder en PNG et SVG plt.savefig('build/enchaînement_intrigues.png', dpi=300, bbox_inches='tight') plt.savefig('build/enchaînement_intrigues.svg', bbox_inches='tight') # Créer un diagramme de Gantt (comme dans gantt_parser.py mais avec des couleurs) fig, ax = plt.subplots(figsize=(12, 8)) # Tracer chaque intrigue avec sa couleur for i, row in df.iterrows(): ax.plot([row['Début'], row['Fin']], [i, i], '-', color=color_dict[row['Intrigue']], linewidth=10) ax.plot([row['Début'], row['Début']], [i-0.1, i+0.1], 'ko') ax.plot([row['Fin'], row['Fin']], [i-0.1, i+0.1], 'ko') # Configurer les axes ax.set_yticks(range(len(df))) ax.set_yticklabels(df['Intrigue']) ax.set_xticks(range(int(df['Début'].min()), int(df['Fin'].max())+1)) ax.set_xticklabels(ax.get_xticks()) ax.set_xlabel('Chapitres') ax.set_title('Diagramme de Gantt des intrigues') # Ajuster les marges plt.tight_layout() # Sauvegarder en PNG et SVG plt.savefig('build/gantt_intrigues.png', dpi=300, bbox_inches='tight') plt.savefig('build/gantt_intrigues.svg', bbox_inches='tight') # Préparer les données pour la page web interactive web_data = [] for i, row in df.iterrows(): web_data.append({ 'intrigue': row['Intrigue'], 'debut': int(row['Début']), 'fin': int(row['Fin']), 'color': color_dict[row['Intrigue']], 'parent': row['Parent'] }) # Sauvegarder les données en JSON with open('build/intrigues_data.json', 'w', encoding='utf-8') as f: json.dump(web_data, f, ensure_ascii=False, indent=2) # Créer la page HTML interactive pour les intrigues html_content = """
Voir l\'analyse détaillée des intrigues
' ) # Ajouter les nouvelles visualisations à la section des intrigues if '' in dashboard_content: dashboard_content = dashboard_content.replace( '', '\n