Export Web/SCORM : corrige la résolution des champs "relation" hors ligne
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:
co-authored by
Claude Sonnet 5
parent
7bc4c89eaa
commit
e08c53e042
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user