Export Web/SCORM : corrige la résolution des champs "relation" hors ligne
Build and deploy / test-python (push) Successful in 6m55s
Build and deploy / test-js (push) Successful in 1m4s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped

Bug signalé : une Donnée liée/un filtre référençant un champ "relation"
affichait "{{champ}}" tel quel une fois exporté, alors qu'il fonctionnait
en ligne. Deux causes cumulées :

- full_game_payload.py lisait la colonne SQL "<champ>" au lieu de
  "<champ>_id" (seule vraie colonne d'un champ relation, voir
  _field_column dans filter_repeater_rows.py) en construisant
  gameData.data — une valeur toujours None. Invisible en ligne (les
  filtres y requêtent la base fraîche, jamais cette snapshot), mais
  fatal hors ligne (aucune base à requêter).
- Une fois ce None corrigé, le port JS (forgeFieldColumn) relisait
  cette même valeur sous une clé slugifiée+suffixée ("boss_id") alors
  que gameData.data est déjà indexé par le nom D'AFFICHAGE du champ
  ("boss") — mismatch qui ne se voyait que sur un champ relation (le
  seul cas où slugify(nom)+suffixe diverge du nom original). Supprime
  ce port erroné, lit directement row[fieldName] partout.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
william
2026-09-02 10:02:26 +02:00
co-authored by Claude Sonnet 5
parent 7bc4c89eaa
commit e08c53e042
7 changed files with 138 additions and 44 deletions
+69
View File
@@ -76,6 +76,75 @@ def test_export_scorm_icon_widget_uses_a_relative_url(client, game):
assert "/static/icons/star.svg" not in html
def test_export_scorm_databound_text_resolves_a_relation_field(client, game):
"""Régression signalée par un utilisateur : un Texte "Donnée liée" (ou
un filtre de Répéteur) referençant un champ "relation" affichait
"{{champ}}" tel quel une fois exporté, alors qu'il fonctionnait très
bien EN LIGNE. Cause racine : full_game_payload.py construisait
gameData.data en lisant la colonne SQL "<champ>" au lieu de
"<champ>_id" (seule vraie colonne d'un champ relation, voir
_field_column dans filter_repeater_rows.py) — une valeur toujours None
qu'EN LIGNE personne ne remarquait puisque filter_repeater_rows.py
requête la base fraîche à chaque rendu, jamais ce snapshot. Le port
hors ligne (static/js/play/offline/), lui, n'a QUE ce snapshot."""
resp = client.post(
f"/game/{game}/objects/new",
data={
"object_name": "Monstre",
"field_name[]": ["nom"],
"field_type[]": ["texte"],
"field_relation[]": [""],
"field_required[]": ["0"],
},
follow_redirects=False,
)
assert resp.status_code == 302, resp.data
monstre_definition_id = int(resp.headers["Location"].rstrip("/").split("/")[-1])
resp = client.post(f"/game/{game}/objects/{monstre_definition_id}/data/new",
data={"nom": "Dragon"}, follow_redirects=False)
assert resp.status_code == 302, resp.data
import db
monstre_row_id = db.list_rows(game, db.get_definition(game, monstre_definition_id))[0]["id"]
resp = client.post(
f"/game/{game}/objects/new",
data={
"object_name": "Niveau",
"field_name[]": ["boss"],
"field_type[]": ["relation"],
"field_relation[]": [str(monstre_definition_id)],
"field_required[]": ["0"],
},
follow_redirects=False,
)
assert resp.status_code == 302, resp.data
definition_id = int(resp.headers["Location"].rstrip("/").split("/")[-1])
resp = client.post(f"/game/{game}/objects/{definition_id}/data/new",
data={"boss_id": str(monstre_row_id)}, follow_redirects=False)
assert resp.status_code == 302, resp.data
import re
resp = client.post(f"/game/{game}/screens/new", data={"name": "Accueil"}, follow_redirects=False)
screen_id = int(re.search(r"/screens/(\d+)/edit", resp.headers["Location"]).group(1))
resp = client.post(f"/game/{game}/screens/{screen_id}/elements/add", data={"widget": "texte"}, follow_redirects=False)
element_id = int(re.search(r"selected=(\d+)", resp.headers["Location"]).group(1))
resp = client.post(
f"/game/{game}/elements/{element_id}/save",
data={"ctrl_content": "Boss : {{boss}}", "ctrl_data_definition_id": str(definition_id)},
follow_redirects=False,
)
assert resp.status_code == 302, resp.data
resp = client.post(f"/game/{game}/export-scorm")
import io
with zipfile.ZipFile(io.BytesIO(resp.data)) as zf:
html = zf.read("index.html").decode("utf-8")
# Le "content" brut ("Boss : {{boss}}") reste forcément présent tel
# quel dans le JSON du payload (c'est le MODÈLE stocké) — seul le
# rendered_html déjà substitué compte ici.
assert f"Boss : {monstre_row_id}" in html
def test_export_scorm_manifest_lists_index_html_as_the_sco(client, game):
resp = client.post(f"/game/{game}/export-scorm")
import io