#!/usr/bin/env python3 # -*- coding: utf-8 -*- import pandas as pd import numpy as np import networkx as nx import matplotlib.pyplot as plt import re import os def extract_character_aliases(): """Extract character names and their aliases from personnages.org""" characters = {} current_character = None try: with open('personnages.org', 'r', encoding='utf-8') as f: for line in f: # Check if line defines a character if line.startswith('**'): current_character = line.strip('* \n').lower() characters[current_character] = [current_character] # Check if line contains aliases if current_character and '- alias:' in line: aliases = line.split('- alias:')[1].strip() if aliases: for alias in re.split(r'[,;]', aliases): alias = alias.strip().lower() if alias: characters[current_character].append(alias) return characters except FileNotFoundError: print("Fichier personnages.org non trouvé.") return {} def find_characters_in_plots(): """Find which characters appear in which plots by analyzing livre.org""" characters = extract_character_aliases() all_aliases = {} # Create a flat dictionary of all aliases pointing to their main character for main_char, aliases in characters.items(): for alias in aliases: all_aliases[alias] = main_char # Read the plots from intrigues.csv plots_df = pd.read_csv('intrigues.csv') # Read the book content try: with open('livre.org', 'r', encoding='utf-8') as f: book_content = f.read().lower() except FileNotFoundError: print("Fichier livre.org non trouvé.") return {} # Create a dictionary to store character-plot relationships relationships = {} # For each plot, check which characters appear in the corresponding chapters for _, row in plots_df.iterrows(): plot_name = row['Intrigue'].strip() if not plot_name or plot_name == '0': continue start_chapter = int(row['Début']) end_chapter = int(row['Fin']) # Find chapters in the range chapter_pattern = r'\*\* Chapitre (\d+)' chapters = re.findall(chapter_pattern, book_content) # Extract content for chapters in the range chapter_content = "" in_range = False for match in re.finditer(r'\*\* Chapitre (\d+)', book_content): chapter_num = int(match.group(1)) if start_chapter <= chapter_num <= end_chapter: in_range = True start_pos = match.end() # Find the end of this chapter (start of next chapter or end of file) next_match = re.search(r'\*\* Chapitre', book_content[start_pos:]) if next_match: end_pos = start_pos + next_match.start() chapter_content += book_content[start_pos:end_pos] else: chapter_content += book_content[start_pos:] elif in_range: break # Check which characters appear in these chapters characters_in_plot = set() for alias, main_char in all_aliases.items(): if alias in chapter_content: characters_in_plot.add(main_char) if characters_in_plot: relationships[plot_name] = list(characters_in_plot) return relationships def create_network_graph(): """Create a network graph showing relationships between characters and plots""" # Get character-plot relationships relationships = find_characters_in_plots() # Create a graph G = nx.Graph() # Add nodes for plots (as squares) for plot in relationships.keys(): G.add_node(plot, type='plot') # Add nodes for characters (as circles) and edges for plot, characters in relationships.items(): for character in characters: G.add_node(character, type='character') G.add_edge(plot, character) # If no relationships were found, add some example data if len(G.nodes()) == 0: # Add plots from intrigues.csv plots_df = pd.read_csv('intrigues.csv') for _, row in plots_df.iterrows(): plot_name = row['Intrigue'].strip() if plot_name and plot_name != '0': G.add_node(plot_name, type='plot') # Add characters from personnages.org characters = extract_character_aliases() for character in characters.keys(): G.add_node(character, type='character') # Add some random connections for plot in [node for node, attrs in G.nodes(data=True) if attrs.get('type') == 'plot']: for character in [node for node, attrs in G.nodes(data=True) if attrs.get('type') == 'character']: if np.random.random() > 0.7: # 30% chance of connection G.add_edge(plot, character) # Create figure plt.figure(figsize=(12, 10)) # Define node positions using spring layout pos = nx.spring_layout(G, k=0.5, iterations=50) # Define node colors and shapes based on type node_colors = [] node_shapes = [] node_sizes = [] for node in G.nodes(): if G.nodes[node]['type'] == 'plot': node_colors.append('#3498db') # Blue for plots node_shapes.append('s') # Square for plots node_sizes.append(1000) # Larger size for plots else: node_colors.append('#e74c3c') # Red for characters node_shapes.append('o') # Circle for characters node_sizes.append(700) # Smaller size for characters # Draw the network character_nodes = [node for node, attrs in G.nodes(data=True) if attrs.get('type') == 'character'] plot_nodes = [node for node, attrs in G.nodes(data=True) if attrs.get('type') == 'plot'] # Draw plot nodes (squares) nx.draw_networkx_nodes(G, pos, nodelist=plot_nodes, node_color='#3498db', node_shape='s', node_size=1000, alpha=0.8) # Draw character nodes (circles) nx.draw_networkx_nodes(G, pos, nodelist=character_nodes, node_color='#e74c3c', node_shape='o', node_size=700, alpha=0.8) # Draw edges nx.draw_networkx_edges(G, pos, width=2, alpha=0.5, edge_color='gray') # Draw labels with custom font sizes nx.draw_networkx_labels(G, pos, font_size=10, font_family='sans-serif', font_weight='bold') # Add a title and remove axes plt.title('Réseau des Personnages et Intrigues', fontsize=16) plt.axis('off') # Add a legend plt.plot([0], [0], 's', color='#3498db', label='Intrigues', markersize=10) plt.plot([0], [0], 'o', color='#e74c3c', label='Personnages', markersize=10) plt.legend(loc='upper right') # Save the figure plt.tight_layout() plt.savefig('reseau_personnages_intrigues.png', dpi=150, bbox_inches='tight') plt.savefig('reseau_personnages_intrigues.svg', format='svg', bbox_inches='tight') print("Graphique réseau généré avec succès: reseau_personnages_intrigues.png et reseau_personnages_intrigues.svg") if __name__ == "__main__": create_network_graph()