Files
Forge-Engine/db/games/move_game.py
T
williamandClaude Sonnet 5 c57420c8c9 Phase 3 : hardening qualite de code - typage strict, securite, dead code, a11y
Config strictement stricte partout (ruff, mypy --strict, bandit, vulture,
import-linter, eslint, stylelint), aucune regle desactivee "pour ne pas
casser le build" - l'existant a ete corrige pour la satisfaire plutot que
l'inverse. Hooks pre-commit locaux (language: system) bloquants.

- Typage mypy --strict propage a tout le moteur (db, screens, auth, core,
  ai, routes, puis publish/scripts/tests/app.py/build_css.py).
- Securite : fuite de handle fichier Windows corrigee dans l'export SCORM
  (routes/publish/export_scorm.py), CSRF/RNG non-crypto/xAPI documentes
  (# nosec, # NOSONAR justifies), nouveau db.json_for_script() (echappe
  "</script>" dans le JSON embarque en <script>, 25 sites).
- Architecture : imports circulaires/F811 nettoyes, contrats
  import-linter respectes, code mort retire (vulture).
- Accessibilite : 69 champs de formulaire sans label correctement
  associe corriges (for/id ou aria-label) sur 11 templates.
- ESLint/Stylelint : lot mecanique JS/CSS, regles ajustees puis
  appliquees (aucune desactivee sans verification individuelle).
- Tests : isolation du compte admin partage (nettoyage ponctuel +
  fixture de teardown automatique en filet de securite), suite complete
  verte (591 tests Python, 241 tests JS).
- SonarQube Community Build self-heberge (Docker + PostgreSQL) : rapport
  complet analyse point par point, faux positifs documentes.
- .gitattributes ajoute (LF force) : core.autocrlf=true sur cette machine
  faisait echouer ESLint (linebreak-style) via un bug connu de git
  (checkout "en place" qui ignore l'eol force sur un fichier deja
  present sur disque - contourne en supprimant puis recreant chaque
  fichier suivi).

djLint (H021, styles inline) volontairement saute pour ce commit -
backlog assume, deja documente, traite dans un lot separe.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-15 16:06:15 +02:00

45 lines
1.9 KiB
Python

import os
from ..constants import PROJECTS_DIR
from ..game_dir import game_dir
from .project_slug import build_slug, split_slug
def move_game(old_slug: str, new_owner_folder: str) -> str:
"""Renomme le dossier PROPRIÉTAIRE d'un compte — owner_folder est
l'id du compte (voir db/games/project_slug.py, routes/games/
games_new.py) : ne change donc plus jamais après coup en usage normal,
mais reste utile pour une migration ponctuelle (voir
scripts/migrate_owner_folders_to_user_id.py, qui a besoin de renommer
les anciens dossiers slugify(email) vers l'id du compte). Renomme le
dossier propriétaire ENTIER en un coup (déplace tous les projets de ce
compte ensemble), pas juste un projet. Rend le nouveau dossier
propriétaire unique de la même façon que create_game() si, par un
hasard extrême, il correspond déjà à un dossier existant. Renvoie le
nouveau slug composé FINAL (même projet, propriétaire renommé), à
enregistrer comme nouveau project_slug."""
old_owner_folder, project_part = split_slug(old_slug)
if project_part is None:
# Slug pas encore migré vers la structure par utilisateur (voir
# scripts/migrate_flat_project_slugs.py) — repli sur l'ancien
# comportement plat (renomme le dossier du projet lui-même).
if old_slug == new_owner_folder:
return old_slug
base = new_owner_folder
n = 2
while os.path.isdir(game_dir(base)):
base = f"{new_owner_folder}-{n}"
n += 1
os.rename(game_dir(old_slug), game_dir(base))
return base
if old_owner_folder == new_owner_folder:
return old_slug
base = new_owner_folder
n = 2
while os.path.isdir(os.path.join(PROJECTS_DIR, base)):
base = f"{new_owner_folder}-{n}"
n += 1
os.rename(os.path.join(PROJECTS_DIR, old_owner_folder), os.path.join(PROJECTS_DIR, base))
return build_slug(base, project_part)