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>
73 lines
4.3 KiB
Python
73 lines
4.3 KiB
Python
import db
|
|
|
|
|
|
def ensure_schema(slug: str) -> None:
|
|
"""Crée les tables de ce module si elles n'existent pas encore — les
|
|
jeux créés avant l'ajout de cette fonctionnalité n'ont pas besoin d'être
|
|
migrés manuellement, la première visite d'un écran de ce jeu suffit."""
|
|
conn = db.connect(slug)
|
|
conn.executescript(
|
|
"""
|
|
CREATE TABLE IF NOT EXISTS _screens (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL,
|
|
order_index INTEGER NOT NULL DEFAULT 0,
|
|
is_template INTEGER NOT NULL DEFAULT 0,
|
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
"""
|
|
)
|
|
screen_cols = {r["name"] for r in conn.execute("PRAGMA table_info(_screens)").fetchall()}
|
|
if "is_template" not in screen_cols:
|
|
# Un écran-modèle héberge le contenu d'un élément de jeu réutilisable
|
|
# (voir template_screen_id ci-dessus) : il n'apparaît jamais dans la
|
|
# liste des écrans du jeu, ni dans le mode jouable, ni dans les
|
|
# menus "aller à cet écran" — seul son contenu (les éléments qu'il
|
|
# contient) est utilisé, jamais l'écran lui-même.
|
|
conn.execute("ALTER TABLE _screens ADD COLUMN is_template INTEGER NOT NULL DEFAULT 0")
|
|
if "preview_aspect" not in screen_cols:
|
|
# Format d'aperçu choisi pour cet écran (Portrait 9/16, Paysage 16/9,
|
|
# ou Carré 1/1) — mémorisé par écran pour qu'il reste le même après
|
|
# un ajout/suppression d'élément ou un changement d'élément
|
|
# sélectionné (ces actions redemandent le panneau au serveur, qui
|
|
# doit donc connaître le choix déjà fait, sinon il repart sur la
|
|
# valeur par défaut à chaque fois). Sert aussi en mode Jouer, pour
|
|
# garder la zone de jeu à la bonne proportion quelle que soit la
|
|
# forme réelle de la fenêtre du joueur (voir #playFrame dans
|
|
# play.html) — sans ça, les positions/tailles en % se déforment dès
|
|
# que la fenêtre du joueur n'a pas la même proportion que celle
|
|
# utilisée à la conception.
|
|
conn.execute("ALTER TABLE _screens ADD COLUMN preview_aspect TEXT NOT NULL DEFAULT '9/16'")
|
|
if "background_music_url" not in screen_cols:
|
|
# Phase 6 — musique de fond de cet écran (URL) : démarrée en boucle
|
|
# à l'affichage de l'écran, arrêtée au changement d'écran (voir
|
|
# runScreenBackgroundMusic() dans static/js/play/screens.js).
|
|
# Indépendante de l'action "Jouer un son" (effet sonore ponctuel,
|
|
# non bouclé, sur n'importe quel déclencheur).
|
|
conn.execute("ALTER TABLE _screens ADD COLUMN background_music_url TEXT")
|
|
if "scene_width" not in screen_cols:
|
|
# Fondations multi-éditeurs — jeux "jeu_2d" (voir db/games/
|
|
# get_game_type.py) : une scène a une taille FIXE en pixels
|
|
# (contrairement à un écran "document", positionné en % fluides
|
|
# via preview_aspect ci-dessus) — indispensable pour la collision
|
|
# et l'animation de sprites. Sans effet sur un écran "document"
|
|
# (jamais lu par son rendu) — voir screens/scenes/.
|
|
conn.execute("ALTER TABLE _screens ADD COLUMN scene_width INTEGER NOT NULL DEFAULT 960")
|
|
conn.execute("ALTER TABLE _screens ADD COLUMN scene_height INTEGER NOT NULL DEFAULT 540")
|
|
if "kind" not in screen_cols:
|
|
# Un seul moteur d'écran désormais : "jeu_2d" (scène pixels — voir
|
|
# db/games/get_game_type.py, conservé pour la rétro-compatibilité
|
|
# d'un jeu créé avant l'existence de cette colonne).
|
|
conn.execute("ALTER TABLE _screens ADD COLUMN kind TEXT NOT NULL DEFAULT 'jeu_2d'")
|
|
conn.execute("UPDATE _screens SET kind = ?", (db.get_game_type(slug),))
|
|
if "_screen_triggers" not in screen_cols:
|
|
# Déclencheurs D'ÉCRAN (voir screens/rendering/screen_triggers.py) —
|
|
# section "Déclencheurs de l'écran" de l'éditeur de scène : SANS
|
|
# objet propriétaire, contrairement à _collision_rules (porté par
|
|
# un objet, colonne JSON dans _scene_objects.attributes). NULL
|
|
# (aucune règle) pour tout écran créé avant l'ajout de cette
|
|
# fonctionnalité, comme pour les autres colonnes ci-dessus.
|
|
conn.execute("ALTER TABLE _screens ADD COLUMN _screen_triggers TEXT")
|
|
conn.commit()
|
|
conn.close()
|