first commit

This commit is contained in:
root
2025-04-20 00:29:58 +02:00
commit 55f66e4544
21 changed files with 640 additions and 0 deletions

144
tree.py Normal file
View File

@ -0,0 +1,144 @@
import pygraphviz as pgv
import json
import os
from PIL import Image, ImageDraw, ImageFont, ImageOps
def create_avatar_with_name(user_id, name, avatar_path, output_path):
"""Crée une image combinant avatar rond et pseudo en dessous"""
try:
# Charge l'avatar original
avatar = Image.open(avatar_path).convert("RGBA")
# Redimensionne et arrondit l'avatar
avatar = avatar.resize((150, 150))
mask = Image.new('L', (150, 150), 0)
draw = ImageDraw.Draw(mask)
draw.ellipse((0, 0, 150, 150), fill=255)
rounded_avatar = ImageOps.fit(avatar, mask.size, centering=(0.5, 0.5))
rounded_avatar.putalpha(mask)
# Crée une nouvelle image avec espace pour le texte
combined = Image.new('RGBA', (200, 200), (0, 0, 0, 0))
combined.paste(rounded_avatar, (25, 0), rounded_avatar)
# Ajoute le texte
draw = ImageDraw.Draw(combined)
try:
font = ImageFont.truetype("arial.ttf", 14)
except:
font = ImageFont.load_default()
text_width = draw.textlength(name, font=font)
draw.text(((200 - text_width) / 2, 160), name, font=font, fill="white")
combined.save(output_path)
return True
except Exception as e:
print(f"Erreur création avatar+texte {user_id}: {e}")
return False
def generate_tree(input_file, output_file, root_id):
with open(input_file) as f:
data = json.load(f)
# Configuration du graphe
G = pgv.AGraph(
directed=True,
rankdir="TB",
nodesep="0.8",
ranksep="1.5",
bgcolor="#000000",
splines="true"
)
# Style minimal pour les nœuds
node_style = {
"shape": "none",
"imagescale": "false",
"labelloc": "b",
"width": "2",
"height": "2",
"fixedsize": "true"
}
# Style des flèches
edge_style = {
"color": "#FFFFFF",
"penwidth": "3",
"arrowsize": "1.2"
}
# Dossier temporaire pour les images combinées
os.makedirs("temp_avatars", exist_ok=True)
# Traitement de tous les membres
for user_id, info in data["members"].items():
original_avatar = f"avatars/{user_id}.png"
combined_avatar = f"temp_avatars/combined_{user_id}.png"
if os.path.exists(original_avatar):
create_avatar_with_name(
user_id,
info["name"],
original_avatar,
combined_avatar
)
G.add_node(
user_id,
image=combined_avatar,
**node_style
)
else:
# Fallback si pas d'avatar
G.add_node(
user_id,
label=info["name"],
**{**node_style, "fontcolor": "white"}
)
# Organisation hiérarchique
levels = {0: [root_id], 1: [], 2: []}
for user_id, info in data["members"].items():
if user_id == root_id:
continue
parents = [p["id"] for p in info.get("parents", [])]
if root_id in parents:
levels[1].append(user_id)
elif any(p in levels[1] for p in parents):
levels[2].append(user_id)
# Ajout des partenaires au bon niveau
for couple in data.get("couples", []):
if all(m in data["members"] for m in couple):
for member in couple:
if member not in levels[1] and any(m in levels[1] for m in couple):
levels[1].append(member)
# Création des sous-graphes par niveau
for level, members in levels.items():
if members:
G.add_subgraph(members, name=f"rank{level}", rank="same")
# Connexions parent-enfant
for user_id, info in data["members"].items():
for parent in info.get("parents", []):
if parent["id"] in data["members"]:
G.add_edge(parent["id"], user_id, **edge_style)
# Connexions des couples
for couple in data.get("couples", []):
if all(m in data["members"] for m in couple):
G.add_edge(couple[0], couple[1],
style="dashed",
color="#FF69B4",
penwidth="2.5",
dir="none")
G.layout(prog="dot")
G.draw(output_file)
# Nettoyage des fichiers temporaires
for f in os.listdir("temp_avatars"):
os.remove(f"temp_avatars/{f}")