(self, langs, include_triggers=False,
                      include_groups=False, include_translations=False,
                      exclude_select_with_itemsets=False, include_fixtures=False)
return questions
parses out the questions from the xform, into the format:
        [{"label": label, "tag": tag, "value": value}, ...]

        if the xform is bad, it will raise an XFormException

        :param langs: A list of language codes - will use the first available language code in
            determining the question's "label". When include_translations=True, it will attempt to
            find a translation for each language in langs, though will only add it if non-null.
        :param include_triggers: When set to True will return label questions as well as regular questions
        :param include_groups: When set will return repeats and group questions
        :param include_translations: When set to True will return all the translations for the question
        :param exclude_select_with_itemsets: exclude select/multi-select with itemsets
        :param include_fixtures: add fixture data for questions that we can infer it from
parses out the questions from the xform, into the format:
        [{"label": label, "tag": tag, "value": value}, ...]
parses out the questions from the xform into the format : [ { label : label tag : tag value : value } ... ]
def get_questions(self, langs, include_triggers=False,
                      include_groups=False, include_translations=False,
                      exclude_select_with_itemsets=False, include_fixtures=False):
        """
        parses out the questions from the xform, into the format:
        [{"label": label, "tag": tag, "value": value}, ...]

        if the xform is bad, it will raise an XFormException

        :param langs: A list of language codes - will use the first available language code in
            determining the question's "label". When include_translations=True, it will attempt to
            find a translation for each language in langs, though will only add it if non-null.
        :param include_triggers: When set to True will return label questions as well as regular questions
        :param include_groups: When set will return repeats and group questions
        :param include_translations: When set to True will return all the translations for the question
        :param exclude_select_with_itemsets: exclude select/multi-select with itemsets
        :param include_fixtures: add fixture data for questions that we can infer it from
        """
        from corehq.apps.app_manager.util import first_elem, extract_instance_id_from_nodeset_ref

        def _get_select_question_option(item):
            translation = self._get_label_text(item, langs)
            try:
                value = item.findtext('{f}value').strip()
            except AttributeError:
                raise XFormException(_("<item> ({}) has no <value>").format(translation))
            option = {
                'label': translation,
                'label_ref': self._get_label_ref(item),
                'value': value,
            }
            if include_translations:
                option['translations'] = self._get_label_translations(item, langs)
            return option

        if not self.exists():
            return []

        questions = []

        # control_nodes will contain all nodes in question tree (the <h:body> of an xform)
        # The question tree doesn't contain every question - notably, it's missing hidden values - so
        # we also need to look at the data tree (the <model> in the xform's <head>). Getting the leaves
        # of the data tree should be sufficient to fill in what's not available from the question tree.
        control_nodes = self._get_control_nodes()
        leaf_data_nodes = self._get_leaf_data_nodes()
        external_instances = self.get_external_instances()

        for cnode in control_nodes:
            node = cnode.node
            path = cnode.path

            is_group = not cnode.is_leaf
            if is_group and not include_groups:
                continue

            if node.tag_name == 'trigger'and not include_triggers:
                continue

            if (exclude_select_with_itemsets and cnode.data_type in ['Select', 'MSelect']
                    and cnode.node.find('{f}itemset').exists()):
                continue

            question = {
                "label": self._get_label_text(node, langs),
                "label_ref": self._get_label_ref(node),
                "tag": node.tag_name,
                "value": path,
                "repeat": cnode.repeat,
                "group": cnode.group,
                "type": cnode.data_type,
                "relevant": cnode.relevant,
                "required": cnode.required == "true()",
                "constraint": cnode.constraint,
                "comment": self._get_comment(path),
                "hashtagValue": self.hashtag_path(path),
                "setvalue": self._get_setvalue(path),
                "is_group": is_group,
            }
            if include_translations:
                question["translations"] = self._get_label_translations(node, langs)

            if include_fixtures and cnode.node.find('{f}itemset').exists():
                itemset_node = cnode.node.find('{f}itemset')
                nodeset = itemset_node.attrib.get('nodeset')
                fixture_data = {
                    'nodeset': nodeset,
                }
                if itemset_node.find('{f}label').exists():
                    fixture_data['label_ref'] = itemset_node.find('{f}label').attrib.get('ref')
                if itemset_node.find('{f}value').exists():
                    fixture_data['value_ref'] = itemset_node.find('{f}value').attrib.get('ref')

                fixture_id = extract_instance_id_from_nodeset_ref(nodeset)
                if fixture_id:
                    fixture_data['instance_id'] = fixture_id
                    fixture_data['instance_ref'] = external_instances.get(fixture_id)

                question['data_source'] = fixture_data

            if cnode.items is not None:
                question['options'] = [_get_select_question_option(item) for item in cnode.items]

            constraint_ref_xml = '{jr}constraintMsg'
            if cnode.constraint and cnode.bind_node.attrib.get(constraint_ref_xml):
                constraint_jr_itext = cnode.bind_node.attrib.get(constraint_ref_xml)
                question['constraintMsg_ref'] = self._normalize_itext_id(constraint_jr_itext)

            questions.append(question)

        repeat_contexts = set()
        group_contexts = set()
        excluded_paths = set()  # prevent adding the same question twice
        for cnode in control_nodes:
            excluded_paths.add(cnode.path)
            if cnode.repeat is not None:
                repeat_contexts.add(cnode.repeat)
            if cnode.data_type == 'Repeat':
                # A repeat is a node inside of a `group`, so it part of both a
                # repeat and a group context
                repeat_contexts.add(cnode.path)
                group_contexts.add(cnode.path)
            if cnode.group is not None:
                group_contexts.add(cnode.group)
            if cnode.data_type == 'Group':
                group_contexts.add(cnode.path)

        repeat_contexts = sorted(repeat_contexts, reverse=True)
        group_contexts = sorted(group_contexts, reverse=True)

        save_to_case_nodes = {}
        for path, data_node in leaf_data_nodes.items():
            if path not in excluded_paths:
                bind = self.get_bind(path)

                matching_repeat_context = first_elem([rc for rc in repeat_contexts
                                                      if path.startswith(rc + '/')])
                matching_group_context = first_elem([gc for gc in group_contexts
                                                     if path.startswith(gc + '/')])

                question = {
                    "tag": "hidden",
                    "value": path,
                    "repeat": matching_repeat_context,
                    "group": matching_group_context,
                    "type": "DataBindOnly",
                    "calculate": bind.attrib.get('calculate') if hasattr(bind, 'attrib') else None,
                    "relevant": bind.attrib.get('relevant') if hasattr(bind, 'attrib') else None,
                    "constraint": bind.attrib.get('constraint') if hasattr(bind, 'attrib') else None,
                    "comment": self._get_comment(path),
                    "setvalue": self._get_setvalue(path)
                }

                # Include meta information about the stock entry
                if data_node.tag_name == 'entry':
                    parent = next(data_node.xml.iterancestors())
                    if len(parent):
                        is_stock_element = any([namespace == COMMTRACK_REPORT_XMLNS for namespace in parent.nsmap.values()])
                        if is_stock_element:
                            question.update({
                                "stock_entry_attributes": dict(data_node.xml.attrib),
                                "stock_type_attributes": dict(parent.attrib),
                            })
                if '/case/' in path:
                    path_to_case = path.split('/case/')[0] + '/case'
                    save_to_case_nodes[path_to_case] = {
                        'data_node': data_node,
                        'repeat': matching_repeat_context,
                        'group': matching_group_context,
                    }

                hashtag_path = self.hashtag_path(path)
                question.update({
                    "label": hashtag_path,
                    "hashtagValue": hashtag_path,
                })

                if include_translations:
                    question["translations"] = {}

                questions.append(question)

        for path, node_info in save_to_case_nodes.items():
            data_node = node_info['data_node']
            try:
                case_node = next(data_node.iterancestors('{cx2}case'))
                for attrib in ('case_id', 'user_id', 'date_modified'):
                    if attrib not in case_node.attrib:
                        continue

                    bind = self.get_bind(path + '/@' + attrib)
                    question = {
                        "tag": "hidden",
                        "value": '{}/@{}'.format(path, attrib),
                        "repeat": node_info['repeat'],
                        "group": node_info['group'],
                        "type": "DataBindOnly",
                        "calculate": None,
                        "relevant": None,
                        "constraint": None,
                        "comment": None,
                    }
                    if bind.exists():
                        question.update({
                            "calculate": bind.attrib.get('calculate') if hasattr(bind, 'attrib') else None,
                            "relevant": bind.attrib.get('relevant') if hasattr(bind, 'attrib') else None,
                            "constraint": bind.attrib.get('constraint') if hasattr(bind, 'attrib') else None,
                        })
                    else:
                        ref = self.model_node.find('{f}setvalue[@ref="%s"]' % path)
                        if ref.exists():
                            question.update({
                                'calculate': ref.attrib.get('value'),
                            })

                    hashtag_path = '{}/@{}'.format(self.hashtag_path(path), attrib)
                    question.update({
                        "label": hashtag_path,
                        "hashtagValue": hashtag_path,
                    })

                    if include_translations:
                        question["translations"] = {}

                    questions.append(question)
            except StopIteration:
                pass

        return questions
def get_questions ( self , langs , include_triggers = False , include_groups = False , include_translations = False , exclude_select_with_itemsets = False , include_fixtures = False ) : from corehq . apps . app_manager . util import first_elem , extract_instance_id_from_nodeset_ref def _get_select_question_option ( item ) : translation = self . _get_label_text ( item , langs ) try : value = item . findtext ( '{f}value' ) . strip ( ) except AttributeError : raise XFormException ( _ ( "<item> ({}) has no <value>" ) . format ( translation ) ) option = { 'label' : translation , 'label_ref' : self . _get_label_ref ( item ) , 'value' : value , } if include_translations : option [ 'translations' ] = self . _get_label_translations ( item , langs ) return option if not self . exists ( ) : return [ ] questions = [ ] # control_nodes will contain all nodes in question tree (the <h:body> of an xform) # The question tree doesn't contain every question - notably, it's missing hidden values - so # we also need to look at the data tree (the <model> in the xform's <head>). Getting the leaves # of the data tree should be sufficient to fill in what's not available from the question tree. control_nodes = self . _get_control_nodes ( ) leaf_data_nodes = self . _get_leaf_data_nodes ( ) external_instances = self . get_external_instances ( ) for cnode in control_nodes : node = cnode . node path = cnode . path is_group = not cnode . is_leaf if is_group and not include_groups : continue if node . tag_name == 'trigger' and not include_triggers : continue if ( exclude_select_with_itemsets and cnode . data_type in [ 'Select' , 'MSelect' ] and cnode . node . find ( '{f}itemset' ) . exists ( ) ) : continue question = { "label" : self . _get_label_text ( node , langs ) , "label_ref" : self . _get_label_ref ( node ) , "tag" : node . tag_name , "value" : path , "repeat" : cnode . repeat , "group" : cnode . group , "type" : cnode . data_type , "relevant" : cnode . relevant , "required" : cnode . required == "true()" , "constraint" : cnode . constraint , "comment" : self . _get_comment ( path ) , "hashtagValue" : self . hashtag_path ( path ) , "setvalue" : self . _get_setvalue ( path ) , "is_group" : is_group , } if include_translations : question [ "translations" ] = self . _get_label_translations ( node , langs ) if include_fixtures and cnode . node . find ( '{f}itemset' ) . exists ( ) : itemset_node = cnode . node . find ( '{f}itemset' ) nodeset = itemset_node . attrib . get ( 'nodeset' ) fixture_data = { 'nodeset' : nodeset , } if itemset_node . find ( '{f}label' ) . exists ( ) : fixture_data [ 'label_ref' ] = itemset_node . find ( '{f}label' ) . attrib . get ( 'ref' ) if itemset_node . find ( '{f}value' ) . exists ( ) : fixture_data [ 'value_ref' ] = itemset_node . find ( '{f}value' ) . attrib . get ( 'ref' ) fixture_id = extract_instance_id_from_nodeset_ref ( nodeset ) if fixture_id : fixture_data [ 'instance_id' ] = fixture_id fixture_data [ 'instance_ref' ] = external_instances . get ( fixture_id ) question [ 'data_source' ] = fixture_data if cnode . items is not None : question [ 'options' ] = [ _get_select_question_option ( item ) for item in cnode . items ] constraint_ref_xml = '{jr}constraintMsg' if cnode . constraint and cnode . bind_node . attrib . get ( constraint_ref_xml ) : constraint_jr_itext = cnode . bind_node . attrib . get ( constraint_ref_xml ) question [ 'constraintMsg_ref' ] = self . _normalize_itext_id ( constraint_jr_itext ) questions . append ( question ) repeat_contexts = set ( ) group_contexts = set ( ) excluded_paths = set ( ) # prevent adding the same question twice for cnode in control_nodes : excluded_paths . add ( cnode . path ) if cnode . repeat is not None : repeat_contexts . add ( cnode . repeat ) if cnode . data_type == 'Repeat' : # A repeat is a node inside of a `group`, so it part of both a # repeat and a group context repeat_contexts . add ( cnode . path ) group_contexts . add ( cnode . path ) if cnode . group is not None : group_contexts . add ( cnode . group ) if cnode . data_type == 'Group' : group_contexts . add ( cnode . path ) repeat_contexts = sorted ( repeat_contexts , reverse = True ) group_contexts = sorted ( group_contexts , reverse = True ) save_to_case_nodes = { } for path , data_node in leaf_data_nodes . items ( ) : if path not in excluded_paths : bind = self . get_bind ( path ) matching_repeat_context = first_elem ( [ rc for rc in repeat_contexts if path . startswith ( rc + '/' ) ] ) matching_group_context = first_elem ( [ gc for gc in group_contexts if path . startswith ( gc + '/' ) ] ) question = { "tag" : "hidden" , "value" : path , "repeat" : matching_repeat_context , "group" : matching_group_context , "type" : "DataBindOnly" , "calculate" : bind . attrib . get ( 'calculate' ) if hasattr ( bind , 'attrib' ) else None , "relevant" : bind . attrib . get ( 'relevant' ) if hasattr ( bind , 'attrib' ) else None , "constraint" : bind . attrib . get ( 'constraint' ) if hasattr ( bind , 'attrib' ) else None , "comment" : self . _get_comment ( path ) , "setvalue" : self . _get_setvalue ( path ) } # Include meta information about the stock entry if data_node . tag_name == 'entry' : parent = next ( data_node . xml . iterancestors ( ) ) if len ( parent ) : is_stock_element = any ( [ namespace == COMMTRACK_REPORT_XMLNS for namespace in parent . nsmap . values ( ) ] ) if is_stock_element : question . update ( { "stock_entry_attributes" : dict ( data_node . xml . attrib ) , "stock_type_attributes" : dict ( parent . attrib ) , } ) if '/case/' in path : path_to_case = path . split ( '/case/' ) [ 0 ] + '/case' save_to_case_nodes [ path_to_case ] = { 'data_node' : data_node , 'repeat' : matching_repeat_context , 'group' : matching_group_context , } hashtag_path = self . hashtag_path ( path ) question . update ( { "label" : hashtag_path , "hashtagValue" : hashtag_path , } ) if include_translations : question [ "translations" ] = { } questions . append ( question ) for path , node_info in save_to_case_nodes . items ( ) : data_node = node_info [ 'data_node' ] try : case_node = next ( data_node . iterancestors ( '{cx2}case' ) ) for attrib in ( 'case_id' , 'user_id' , 'date_modified' ) : if attrib not in case_node . attrib : continue bind = self . get_bind ( path + '/@' + attrib ) question = { "tag" : "hidden" , "value" : '{}/@{}' . format ( path , attrib ) , "repeat" : node_info [ 'repeat' ] , "group" : node_info [ 'group' ] , "type" : "DataBindOnly" , "calculate" : None , "relevant" : None , "constraint" : None , "comment" : None , } if bind . exists ( ) : question . update ( { "calculate" : bind . attrib . get ( 'calculate' ) if hasattr ( bind , 'attrib' ) else None , "relevant" : bind . attrib . get ( 'relevant' ) if hasattr ( bind , 'attrib' ) else None , "constraint" : bind . attrib . get ( 'constraint' ) if hasattr ( bind , 'attrib' ) else None , } ) else : ref = self . model_node . find ( '{f}setvalue[@ref="%s"]' % path ) if ref . exists ( ) : question . update ( { 'calculate' : ref . attrib . get ( 'value' ) , } ) hashtag_path = '{}/@{}' . format ( self . hashtag_path ( path ) , attrib ) question . update ( { "label" : hashtag_path , "hashtagValue" : hashtag_path , } ) if include_translations : question [ "translations" ] = { } questions . append ( question ) except StopIteration : pass return questions