Ajoute le support de formation : entite racine separee du jeu 2D
Build and deploy / test-python (push) Successful in 11m2s
Build and deploy / test-js (push) Successful in 1m2s
Build and deploy / lint-python (push) Successful in 4m6s
Build and deploy / lint-js (push) Failing after 1m21s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Build and deploy / sonarqube (push) Failing after 4m2s

Nouveau moteur document_engine/ (elements CRUD + rendering + labels),
db/supports/ (stockage independant de db/games), routes/document/
(CRUD AJAX + publication), et l'editeur frontend complet
(templates/document/, static/document/) avec moteur de layout reel
(glisser-deposer -> fusion en rangee ou insertion avant/apres), vrai
Undo/Redo par pile de commandes, grille d'accroche pour les formes
libres, apercu responsive a largeurs fixes, mode Apercu, et publication
persistee.

"Mes formations" (templates/index.html) liste desormais les
environnements 2D et les supports de formation cote a cote ;
l'onboarding et core/auth_guard.py sont generalises pour qu'un compte
restreint puisse posseder un projet de chaque type independamment.

SKIP=djlint : le hook ne signale que le backlog H021 (styles en ligne)
deja documente dans CODE_QUALITY.md sur des fichiers pre-existants non
touches ici (base.html, game/play.html, scene_edit.html,
game_dashboard_simple.html, clause_row.html) plus une ligne de
index.html deja presente avant cette session — aucun nouveau fichier
(document_edit.html compris) n'y figure. Tous les autres outils
(ruff, mypy --strict, vulture, bandit, import-linter, eslint,
stylelint) passent sans erreur ; 617 tests Python + 276 tests JS
verts, plus une verification manuelle complete du cycle de vie via le
serveur de developpement.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
william
2026-09-20 09:20:33 +02:00
co-authored by Claude Sonnet 5
parent 7e4a761cbb
commit bab581737a
59 changed files with 4158 additions and 142 deletions
+57
View File
@@ -0,0 +1,57 @@
# document_engine/elements/
CRUD des éléments d'un support de formation (`_document_elements`, voir
`db/supports/create_support.py`). Un élément est soit un élément de
contenu/rangée du flux (`parent_id` = groupe de frères), soit une forme
libre superposée en position absolue (toujours `parent_id = NULL`).
## `add_document_element(slug: str, kind: str, parent_id: int | None = None) -> int`
Insère un nouvel élément en fin de son groupe de frères (même `parent_id`).
Pose les attributs de départ via `element_default_attributes(kind)`
(voir `document_engine/labels/element_kind_labels.py`).
- **Retour** : l'`id` du nouvel élément.
- **Exceptions** : aucune levée explicitement ; une connexion invalide
(support inexistant) lève l'erreur SQLite sous-jacente.
## `list_document_elements(slug: str) -> list[dict[str, Any]]`
Renvoie tous les éléments du support, à plat, triés par `(parent_id,
order_index)` — les éléments top-niveau (`parent_id` NULL) groupés en
premier (tri SQLite : NULL avant toute valeur), puis chaque rangée
groupant ses propres enfants. `attributes` est décodé en dict.
- **Retour** : liste de dicts (une ligne de table chacun, `attributes`
déjà en JSON décodé).
- **Exceptions** : aucune.
## `get_document_element(slug: str, element_id: int) -> dict[str, Any] | None`
Récupère un seul élément par id.
- **Retour** : le dict de l'élément, ou `None` si l'id n'existe pas.
- **Exceptions** : aucune.
## `update_document_element_attributes(slug: str, element_id: int, attributes: dict[str, Any]) -> None`
Remplace intégralement le JSON `attributes` d'un élément — chaque
formulaire du panneau Propriétés envoie l'état complet de ses champs,
jamais un patch partiel.
- **Retour** : aucun.
- **Exceptions** : aucune levée explicitement ; un `element_id`
inexistant ne modifie silencieusement aucune ligne (`UPDATE` sans
correspondance).
## `move_document_element(slug: str, element_id: int, new_parent_id: int | None, new_index: int) -> None`
Réinsertion réelle appelée par l'algorithme de glisser-déposer du moteur
de layout : dépose l'élément dans un groupe de frères (nouvelle rangée,
un groupe existant, ou le top-niveau) à une position précise, puis
renumérote intégralement le ou les groupes concernés (ancien et nouveau
si le parent change, un seul sinon) pour rester correct même en cas de
réordonnancement dans le même groupe.
- **Retour** : aucun.
- **Exceptions** : aucune levée explicitement ; un `element_id`
inexistant ne fait rien (retour silencieux après vérification de son
existence).
## `delete_document_element(slug: str, element_id: int) -> None`
Supprime un élément. La contrainte `FOREIGN KEY ... ON DELETE CASCADE`
(voir `db/supports/create_support.py`) retire automatiquement ses
enfants si l'élément supprimé était une rangée.
- **Retour** : aucun.
- **Exceptions** : aucune levée explicitement ; un `element_id`
inexistant ne modifie silencieusement aucune ligne.