Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed duplicate label translations for secondary itemsets and use itextID for selects with choices that have media specified #468

Merged
merged 9 commits into from
Sep 9, 2020
7 changes: 6 additions & 1 deletion pyxform/question.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,11 +195,16 @@ def build_xml(self):
if self["itemset"] and isinstance(self["itemset"], basestring):
choice_filter = self.get("choice_filter")
itemset, file_extension = os.path.splitext(self["itemset"])
has_media = False

if choices.get(itemset):
has_media = bool(choices[itemset][0].get("media"))

if file_extension in [".csv", ".xml"]:
itemset = itemset
itemset_label_ref = "label"
else:
if not multi_language:
if not multi_language and not has_media:
itemset = self["itemset"]
itemset_label_ref = "label"
else:
Expand Down
78 changes: 51 additions & 27 deletions pyxform/survey.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,12 +227,13 @@ def _generate_static_instances(list_name, choice_list):
"""
instance_element_list = []
multi_language = isinstance(choice_list[0].get("label"), dict)
has_media = bool(choice_list[0].get("media"))
for idx, choice in enumerate(choice_list):
choice_element_list = []
# Add a unique id to the choice element in case there is itext
# it references
if multi_language:
itext_id = "-".join(["static_instance", list_name, str(idx)])
lognaturel marked this conversation as resolved.
Show resolved Hide resolved
if multi_language or has_media:
itext_id = "-".join([list_name, str(idx)])
choice_element_list.append(node("itextId", itext_id))

for name, value in sorted(choice.items()):
Expand Down Expand Up @@ -590,31 +591,41 @@ def _setup_choice_translations(name, choice_value, itext_id):

self._translations = defaultdict(dict) # pylint: disable=W0201
for element in self.iter_descendants():
for d in element.get_translations(self.default_language):

translation_path = d["path"]
form = "long"

if "guidance_hint" in d["path"]:
translation_path = d["path"].replace("guidance_hint", "hint")
form = "guidance"

self._translations[d["lang"]][translation_path] = self._translations[
d["lang"]
].get(translation_path, {})

self._translations[d["lang"]][translation_path].update(
{form: {"text": d["text"], "output_context": d["output_context"],}}
)
# Skip creation of translations for choices in filtered selects
# The creation of these translations is done futher below in this
# function
parent = element.get("parent")
if parent and not parent.get("choice_filter"):
for d in element.get_translations(self.default_language):
translation_path = d["path"]
form = "long"

if "guidance_hint" in d["path"]:
translation_path = d["path"].replace("guidance_hint", "hint")
form = "guidance"

self._translations[d["lang"]][
translation_path
] = self._translations[d["lang"]].get(translation_path, {})

self._translations[d["lang"]][translation_path].update(
{
form: {
"text": d["text"],
"output_context": d["output_context"],
}
}
)

# This code sets up translations for choices in filtered selects.
for list_name, choice_list in self.choices.items():
multi_language = isinstance(choice_list[0].get("label"), dict)
if not multi_language:
has_media = bool(choice_list[0].get("media"))
if not multi_language and not has_media:
continue
for idx, choice in zip(range(len(choice_list)), choice_list):
for name, choice_value in choice.items():
itext_id = "-".join(["static_instance", list_name, str(idx)])
itext_id = "-".join([list_name, str(idx)])
if isinstance(choice_value, dict):
_setup_choice_translations(name, choice_value, itext_id)
elif name == "label":
Expand Down Expand Up @@ -651,14 +662,8 @@ def _setup_media(self):
{language : {element_xpath : {media_type : media}}}
It matches the xform nesting order.
"""
if not self._translations:
self._translations = defaultdict(dict) # pylint: disable=W0201

for survey_element in self.iter_descendants():

translation_key = survey_element.get_xpath() + ":label"
media_dict = survey_element.get("media")

def _set_up_media_translations(media_dict, translation_key):
# This is probably papering over a real problem, but anyway,
# in py3, sometimes if an item is on an xform with multiple
# languages and the item only has media defined in # "default"
Expand Down Expand Up @@ -702,6 +707,25 @@ def _setup_media(self):

translations_trans_key[media_type] = media

if not self._translations:
self._translations = defaultdict(dict) # pylint: disable=W0201

for survey_element in self.iter_descendants():
parent = survey_element.get("parent")
if parent and not parent.get("choice_filter"):
lognaturel marked this conversation as resolved.
Show resolved Hide resolved
translation_key = survey_element.get_xpath() + ":label"
media_dict = survey_element.get("media")
_set_up_media_translations(media_dict, translation_key)

# This code sets up media for choices in filtered selects.
lognaturel marked this conversation as resolved.
Show resolved Hide resolved
for list_name, choice_list in self.choices.items():
has_media = bool(choice_list[0].get("media"))
if not has_media:
continue
for idx, choice in zip(range(len(choice_list)), choice_list):
itext_id = "-".join([list_name, str(idx)])
_set_up_media_translations(choice.get("media"), itext_id)

def itext(self):
"""
This function creates the survey's itext nodes from _translations
Expand Down
92 changes: 80 additions & 12 deletions pyxform/tests_v1/test_inline_translations.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ def test_inline_translations(self):
"<label>c</label>",
],
model__excludes=[
'<text id="static_instance-states-0">',
'<text id="static_instance-states-1">',
'<text id="static_instance-states-2">',
"<itextId>static_instance-states-0</itextId>",
"<itextId>static_instance-states-1</itextId>",
"<itextId>static_instance-states-2</itextId>",
'<text id="states-0">',
'<text id="states-1">',
'<text id="states-2">',
"<itextId>states-0</itextId>",
"<itextId>states-1</itextId>",
"<itextId>states-2</itextId>",
],
xml__contains=['<label ref="label"/>'],
xml__excludes=['<label ref="jr:itext(itextId)"/>'],
Expand All @@ -59,12 +59,12 @@ def test_multiple_translations(self):
name="data",
id_string="some-id",
model__contains=[
'<text id="static_instance-states-0">',
'<text id="static_instance-states-1">',
'<text id="static_instance-states-2">',
"<itextId>static_instance-states-0</itextId>",
"<itextId>static_instance-states-1</itextId>",
"<itextId>static_instance-states-2</itextId>",
'<text id="states-0">',
'<text id="states-1">',
'<text id="states-2">',
"<itextId>states-0</itextId>",
"<itextId>states-1</itextId>",
"<itextId>states-2</itextId>",
],
model__excludes=[
"<label>a</label>",
Expand All @@ -74,3 +74,71 @@ def test_multiple_translations(self):
xml__contains=['<label ref="jr:itext(itextId)"/>'],
xml__excludes=['<label ref="label"/>'],
)

def test_select_with_media_and_choice_filter_and_no_translations_generates_media(
self,
):
"""
Selects with media and choice filter should generate itext fields.
"""
xform_md = """
| survey | | | | |
| | type | name | label | choice_filter |
| | select_one consent | consent | Would you like to participate ? | |
| | select_one mood | enumerator_mood | How are you feeling today ? | selected(${consent}, 'y') |
| choices |
| | list_name | name | label | media::image |
| | mood | h | Happy | happy.jpg |
| | mood | s | Sad | sad.jpg |
| | consent | y | Yes | |
| | consent | n | No | |
"""
self.assertPyxformXform(
name="data",
id_string="some-id",
md=xform_md,
errored=False,
debug=False,
model__contains=[
'<text id="mood-0">',
'<text id="mood-1">',
"<itextId>mood-0</itextId>",
"<itextId>mood-1</itextId>",
],
xml__contains=['<label ref="jr:itext(itextId)"/>'],
xml__excludes=['<label ref="label"/>'],
)

def test_select_with_choice_filter_and_translations_generates_single_translation(
self,
):
"""
Selects with choice filter and translations should only have a single itext entry.
"""
xform_md = """
| survey | | | | |
| | type | name | label | choice_filter |
| | select_one list | foo | Foo | name != " |
| choices |
| | list_name | name | label | image | label::French |
| | list | a | A | a.jpg | Ah |
| | list | b | B | b.jpg | Bé |
| | list | c | C | c.jpg | Cé |
"""
self.assertPyxformXform(
name="data",
id_string="some-id",
md=xform_md,
errored=False,
debug=False,
itext__contains=[
'<text id="list-0">',
'<text id="list-1">',
'<text id="list-2">',
],
itext__excludes=[
'<text id="/data/foo/a:label">',
'<text id="/data/foo/b:label">',
'<text id="/data/foo/c:label">',
],
)