"""Convention du slug composé "propriétaire_projet" (voir le plan "Structure de dossiers propriétaire/projet") : un slug encode en réalité deux segments — le dossier PROPRIÉTAIRE (slugifié depuis l'email du compte qui a créé le projet) et le dossier PROJET à l'intérieur — pour que projects/ soit une vraie arborescence par utilisateur (projects///), sans jamais toucher aux ~40 fichiers de routes qui déclarent dans leur URL : le slug reste, pour elles, un seul segment plat et opaque. "_" comme séparateur est sûr et non ambigu : slugify() (voir db/slugify.py) ne produit jamais de "_", seulement [a-z0-9-]+ — un slug composé peut donc toujours être découpé sans collision sur la PREMIÈRE occurrence de "_" (le dossier propriétaire, lui, n'en contient jamais). Seule une poignée de fonctions dans db/games/ (game_dir, create_game, move_game, list_games) savent que le slug encode ce chemin composé — si la convention change un jour, ce fichier est le seul à modifier.""" def build_slug(owner_folder, project_part): return f"{owner_folder}_{project_part}" def split_slug(slug): """(owner_folder, project_part) si `slug` est bien composé, sinon (slug, None) — un slug "plat" (créé avant cette convention, pas encore migré par scripts/migrate_flat_project_slugs.py) reste lisible tel quel par le code appelant (repli sur l'ancien comportement).""" owner_folder, sep, project_part = slug.partition("_") if not sep: return slug, None return owner_folder, project_part