import json import os from typing import Dict, List, Optional class FamilyManager: def __init__(self, file_path: str): self.file_path = file_path if not os.path.exists(file_path): self._init_empty_file() def _init_empty_file(self): """Initialise un fichier JSON vide avec la structure attendue""" with open(self.file_path, "w") as f: json.dump({ "members": {}, "couples": [], "roots": [] }, f, indent=2) def _load(self) -> Dict: """Charge les données depuis le fichier JSON""" with open(self.file_path) as f: return json.load(f) def _save(self, data: Dict): """Sauvegarde les données dans le fichier JSON""" with open(self.file_path, "w") as f: json.dump(data, f, indent=2) def add_member(self, member_id: str, member_name: str, avatar_url: str = None): """ Ajoute ou met à jour un membre :param member_id: ID Discord du membre :param member_name: Nom d'affichage :param avatar_url: URL de l'avatar (optionnel) """ data = self._load() if str(member_id) not in data["members"]: data["members"][str(member_id)] = { "name": member_name, "avatar": avatar_url, "parents": [] } else: # Mise à jour des informations existantes data["members"][str(member_id)]["name"] = member_name if avatar_url: data["members"][str(member_id)]["avatar"] = avatar_url self._save(data) def add_child(self, parent_id: str, child_id: str) -> bool: """ Ajoute un lien parent-enfant :param parent_id: ID du parent :param child_id: ID de l'enfant :return: True si succès, False si échec """ data = self._load() # Vérifie que les membres existent if str(child_id) not in data["members"] or str(parent_id) not in data["members"]: return False # Ajoute le parent principal parent_name = data["members"][str(parent_id)]["name"] data["members"][str(child_id)]["parents"].append({ "id": str(parent_id), "name": parent_name }) # Ajoute le partenaire comme parent secondaire si existe partner_id = self.get_partner(parent_id) if partner_id: partner_name = data["members"].get(partner_id, {}).get("name", "Partenaire") data["members"][str(child_id)]["parents"].append({ "id": partner_id, "name": partner_name }) self._save(data) return True def add_couple(self, member1_id: str, member2_id: str) -> bool: """ Crée un lien de couple entre deux membres :return: True si le couple a été ajouté, False s'il existait déjà """ data = self._load() couple = sorted([str(member1_id), str(member2_id)]) if couple not in data["couples"]: data["couples"].append(couple) self._save(data) return True return False def get_partner(self, member_id: str) -> Optional[str]: """Trouve l'ID du partenaire d'un membre""" data = self._load() for couple in data["couples"]: if str(member_id) in couple: return couple[0] if couple[1] == str(member_id) else couple[1] return None def remove_child(self, parent_id: str, child_id: str) -> bool: """ Supprime un lien parent-enfant :return: True si suppression effectuée, False si lien introuvable """ data = self._load() changed = False if str(child_id) in data["members"]: original_count = len(data["members"][str(child_id)]["parents"]) data["members"][str(child_id)]["parents"] = [ p for p in data["members"][str(child_id)]["parents"] if p["id"] != str(parent_id) ] changed = len(data["members"][str(child_id)]["parents"]) < original_count if changed: self._save(data) return changed def remove_member(self, member_id: str) -> bool: """Supprime complètement un membre de la famille""" data = self._load() member_id = str(member_id) if member_id not in data["members"]: return False # Supprime des couples data["couples"] = [c for c in data["couples"] if member_id not in c] # Supprime des racines if "roots" in data and member_id in data["roots"]: data["roots"].remove(member_id) # Supprime le membre del data["members"][member_id] # Nettoie les références parentales for m in data["members"].values(): m["parents"] = [p for p in m["parents"] if p["id"] != member_id] self._save(data) return True def get_member(self, member_id: str) -> Optional[Dict]: """Récupère les informations d'un membre""" data = self._load() return data["members"].get(str(member_id)) def get_parents(self, member_id: str) -> List[Dict]: """Liste les parents d'un membre""" data = self._load() return data["members"].get(str(member_id), {}).get("parents", []) def get_children(self, member_id: str) -> List[str]: """Liste les enfants d'un membre""" data = self._load() return [ user_id for user_id, info in data["members"].items() if any(p["id"] == str(member_id) for p in info.get("parents", [])) ] def get_generation(self, member_id: str) -> int: """ Calcule la génération d'un membre :return: 0 pour les racines, 1 pour leurs enfants, etc. """ data = self._load() # Si c'est une racine explicite if str(member_id) in data.get("roots", []): return 0 parents = data["members"].get(str(member_id), {}).get("parents", []) # Si pas de parents => nouvelle racine (gen 0) if not parents: return 0 # Calcul basé sur les parents return 1 + min( self.get_generation(p["id"]) for p in parents if p["id"] in data["members"] ) def get_all_members(self) -> List[str]: """Liste tous les IDs des membres enregistrés""" data = self._load() return list(data["members"].keys()) def get_roots(self) -> List[str]: """Liste toutes les racines""" data = self._load() return data.get("roots", [])