def _generate_static_instances(list_name, choice_list): """ Generates <instance> elements for static data (e.g. choices for select type questions) Note that per commit message 0578242 and in xls2json.py R539, an instance is only output for select items defined in the choices sheet when the item has a choice_filter, and it is that way for backwards compatibility. """ 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 or has_media or has_dynamic_label(choice_list, multi_language) ): itext_id = "-".join([list_name, str(idx)]) choice_element_list.append(node("itextId", itext_id)) for name, value in sorted(choice.items()): if isinstance(value, basestring) and name != "label": choice_element_list.append(node(name, unicode(value))) if ( not multi_language and not has_media and not has_dynamic_label(choice_list, multi_language) and isinstance(value, basestring) and name == "label" ): choice_element_list.append(node(name, unicode(value))) instance_element_list.append(node("item", *choice_element_list)) return InstanceInfo( type="choice", context="survey", name=list_name, src=None, instance=node( "instance", node("root", *instance_element_list), id=list_name ), )
def _setup_translations(self): """ set up the self._translations dict which will be referenced in the setup media and itext functions """ def _setup_choice_translations(name, choice_value, itext_id): for media_type_or_language, value in choice_value.items(): # noqa if isinstance(value, dict): for language, val in value.items(): self._add_to_nested_dict( self._translations, [language, itext_id, media_type_or_language], val, ) else: if name == "media": self._add_to_nested_dict( self._translations, [self.default_language, itext_id, media_type_or_language], value, ) else: self._add_to_nested_dict( self._translations, [media_type_or_language, itext_id, "long"], value, ) self._translations = defaultdict(dict) # pylint: disable=W0201 for element in self.iter_descendants(): # 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) has_media = bool(choice_list[0].get("media")) if ( not multi_language and not has_media and not has_dynamic_label(choice_list, multi_language) ): continue for idx, choice in zip(range(len(choice_list)), choice_list): for name, choice_value in choice.items(): itext_id = "-".join([list_name, str(idx)]) if isinstance(choice_value, dict): _setup_choice_translations(name, choice_value, itext_id) elif name == "label": self._add_to_nested_dict( self._translations, [self.default_language, itext_id, "long"], choice_value, )
def build_xml(self): assert self.bind["type"] in ["string", "odk:rank"] survey = self.get_root() control_dict = self.control.copy() # Resolve field references in attributes for key, value in control_dict.items(): control_dict[key] = survey.insert_xpaths(value, self) control_dict["ref"] = self.get_xpath() result = node(**control_dict) for element in self.xml_label_and_hint(): result.appendChild(element) choices = survey.get("choices") multi_language = False if choices is not None and len(choices) > 0: first_choices = next(iter(choices.values())) multi_language = isinstance(first_choices[0].get("label"), dict) # itemset are only supposed to be strings, # check to prevent the rare dicts that show up if self["itemset"] and isinstance(self["itemset"], str): choice_filter = self.get("choice_filter") itemset, file_extension = os.path.splitext(self["itemset"]) itemset_value_ref = self.parameters.get( "value", EXTERNAL_CHOICES_ITEMSET_REF_VALUE_GEOJSON if file_extension == ".geojson" else EXTERNAL_CHOICES_ITEMSET_REF_VALUE, ) itemset_label_ref = self.parameters.get( "label", EXTERNAL_CHOICES_ITEMSET_REF_LABEL_GEOJSON if file_extension == ".geojson" else EXTERNAL_CHOICES_ITEMSET_REF_LABEL, ) has_media = False has_dyn_label = False is_previous_question = bool( re.match(r"^\${.*}$", self.get("itemset"))) if choices.get(itemset): has_media = bool(choices[itemset][0].get("media")) has_dyn_label = has_dynamic_label(choices[itemset], multi_language) if file_extension in EXTERNAL_INSTANCE_EXTENSIONS: itemset = itemset else: if not multi_language and not has_media and not has_dyn_label: itemset = self["itemset"] else: itemset = self["itemset"] itemset_label_ref = "jr:itext(itextId)" choice_filter = survey.insert_xpaths(choice_filter, self, True, is_previous_question) if is_previous_question: path = (survey.insert_xpaths( self["itemset"], self, reference_parent=True).strip().split("/")) nodeset = "/".join(path[:-1]) itemset_value_ref = path[-1] itemset_label_ref = path[-1] if choice_filter: choice_filter = choice_filter.replace( "current()/" + nodeset, ".").replace(nodeset, ".") else: # Choices must have a value. Filter out repeat instances without # an answer for the linked question name = path[-1] choice_filter = f"./{name} != ''" else: nodeset = "instance('" + itemset + "')/root/item" if choice_filter: nodeset += "[" + choice_filter + "]" if self["parameters"]: params = self["parameters"] if "randomize" in params and params["randomize"] == "true": nodeset = "randomize(" + nodeset if "seed" in params: if params["seed"].startswith("${"): nodeset = (nodeset + ", " + survey.insert_xpaths( params["seed"], self).strip()) else: nodeset = nodeset + ", " + params["seed"] nodeset += ")" itemset_children = [ node("value", ref=itemset_value_ref), node("label", ref=itemset_label_ref), ] result.appendChild( node("itemset", *itemset_children, nodeset=nodeset)) else: for child in self.children: result.appendChild(child.xml()) return result