125 lines
6.7 KiB
Python
125 lines
6.7 KiB
Python
import db
|
|
|
|
|
|
def ensure_schema(slug):
|
|
"""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
|
|
);
|
|
CREATE TABLE IF NOT EXISTS _element_types (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL UNIQUE,
|
|
icon TEXT NOT NULL DEFAULT '🧩',
|
|
default_widget TEXT NOT NULL DEFAULT 'conteneur',
|
|
default_content TEXT NOT NULL DEFAULT '',
|
|
default_attributes TEXT NOT NULL DEFAULT '{}',
|
|
default_style TEXT NOT NULL DEFAULT '{}',
|
|
template_screen_id INTEGER REFERENCES _screens(id),
|
|
bound_definition_id INTEGER,
|
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE TABLE IF NOT EXISTS _screen_elements (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
screen_id INTEGER NOT NULL REFERENCES _screens(id) ON DELETE CASCADE,
|
|
parent_id INTEGER REFERENCES _screen_elements(id) ON DELETE CASCADE,
|
|
widget TEXT NOT NULL DEFAULT 'conteneur',
|
|
element_type_id INTEGER REFERENCES _element_types(id),
|
|
name TEXT,
|
|
content TEXT NOT NULL DEFAULT '',
|
|
attributes TEXT NOT NULL DEFAULT '{}',
|
|
x REAL NOT NULL DEFAULT 10,
|
|
y REAL NOT NULL DEFAULT 10,
|
|
width REAL NOT NULL DEFAULT 20,
|
|
height REAL NOT NULL DEFAULT 10,
|
|
z_index INTEGER NOT NULL DEFAULT 1,
|
|
style TEXT NOT NULL DEFAULT '{}',
|
|
created_at TEXT DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE TABLE IF NOT EXISTS _actions (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
element_id INTEGER NOT NULL REFERENCES _screen_elements(id) ON DELETE CASCADE,
|
|
trigger_event TEXT NOT NULL,
|
|
action_type TEXT NOT NULL,
|
|
target_screen_id INTEGER REFERENCES _screens(id),
|
|
target_element_id INTEGER REFERENCES _screen_elements(id),
|
|
element_property TEXT,
|
|
element_value TEXT,
|
|
target_definition_id INTEGER,
|
|
target_row_id INTEGER,
|
|
target_field TEXT,
|
|
data_operation TEXT,
|
|
data_value TEXT
|
|
);
|
|
"""
|
|
)
|
|
cols = {r["name"] for r in conn.execute("PRAGMA table_info(_screen_elements)").fetchall()}
|
|
if "widget" not in cols:
|
|
conn.execute("ALTER TABLE _screen_elements ADD COLUMN widget TEXT NOT NULL DEFAULT 'conteneur'")
|
|
if "attributes" not in cols:
|
|
conn.execute("ALTER TABLE _screen_elements ADD COLUMN attributes TEXT NOT NULL DEFAULT '{}'")
|
|
if "parent_id" not in cols:
|
|
conn.execute("ALTER TABLE _screen_elements ADD COLUMN parent_id INTEGER REFERENCES _screen_elements(id) ON DELETE CASCADE")
|
|
if "name" not in cols:
|
|
conn.execute("ALTER TABLE _screen_elements ADD COLUMN name TEXT")
|
|
type_cols = {r["name"] for r in conn.execute("PRAGMA table_info(_element_types)").fetchall()}
|
|
if "default_widget" not in type_cols:
|
|
conn.execute("ALTER TABLE _element_types ADD COLUMN default_widget TEXT NOT NULL DEFAULT 'conteneur'")
|
|
conn.execute("ALTER TABLE _element_types ADD COLUMN default_content TEXT NOT NULL DEFAULT ''")
|
|
conn.execute("ALTER TABLE _element_types ADD COLUMN default_attributes TEXT NOT NULL DEFAULT '{}'")
|
|
if "template_screen_id" not in type_cols:
|
|
# Un élément de jeu n'est plus un simple widget isolé : c'est un
|
|
# conteneur avec un vrai arbre d'éléments imbriqués à l'intérieur,
|
|
# construit exactement comme le contenu de n'importe quel conteneur
|
|
# sur un écran — ce petit arbre vit dans un écran-modèle CACHÉ
|
|
# (voir "is_template" ci-dessous), pointé par cette colonne.
|
|
conn.execute("ALTER TABLE _element_types ADD COLUMN template_screen_id INTEGER REFERENCES _screens(id)")
|
|
if "bound_definition_id" not in type_cols:
|
|
# "Objet lié" optionnel — comme pour le Répéteur de données, rend
|
|
# {{nom_du_champ}} disponible dans tout l'arbre de ce modèle.
|
|
conn.execute("ALTER TABLE _element_types ADD COLUMN bound_definition_id INTEGER")
|
|
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'")
|
|
action_cols = {r["name"] for r in conn.execute("PRAGMA table_info(_actions)").fetchall()}
|
|
for col_name, col_def in [
|
|
("target_element_id", "INTEGER REFERENCES _screen_elements(id)"),
|
|
("element_property", "TEXT"),
|
|
("element_value", "TEXT"),
|
|
("target_definition_id", "INTEGER"),
|
|
("target_row_id", "INTEGER"),
|
|
("target_field", "TEXT"),
|
|
("data_operation", "TEXT"),
|
|
("data_value", "TEXT"),
|
|
]:
|
|
if col_name not in action_cols:
|
|
conn.execute(f"ALTER TABLE _actions ADD COLUMN {col_name} {col_def}")
|
|
conn.commit()
|
|
conn.close()
|