Beispiel #1
0
        def end_pending_group(self):
            """End a pending group.

            A pending group is a group on the pending stack. This is not a
            context group. This function is only called internally in response
            to receiving and dealing with an 'end group' type.

            If the pending group is nested in a repeat, then it is added to
            that repeat.

            Raises:
                OdkFormError: If the parsing rules are broken based on the
                    current context.

            """
            if self.pending_stack:
                last_pending = self.pending_stack.pop()
                if not isinstance(last_pending, OdkGroup):
                    msg = "Found end group but no group in pending stack"
                    raise OdkFormError(msg)
                last_pending.add_pending()
                if self.pending_stack:
                    self.pending_stack[-1].add(last_pending)
                else:
                    self.result.append(last_pending)
            else:
                msg = "Found end group but nothing pending stack."
                raise OdkFormError(msg)
Beispiel #2
0
    def parse_select_type(row, choices, ext_choices):
        """Extract relevant information from a select_* ODK prompt.

        Build a dictionary that distills the main details of the row. The
        select type questions can have a token type of 'prompt' or 'table'.
        The prompt type is default, and table type is if the appearance of the
        question has either 'label' or 'list-nolabel'.

        Args:
            row (dict): A row as a dictionary. Keys and values are strings.
            choices (dict): A diction   ary with list_names as keys. Represents
                the choices found in 'choices' tab.
            ext_choices (dict): A dictionary with list_names as keys.
                Represents choices found in 'external_choices' tab.

        Returns:
            A dictionary with the simple information about this prompt.

        Raises:
            OdkFormError: If the row is not select_[one|multiple](_external)?
            KeyError: If the select question's choice list is not found.
        """
        simple_row = {"token_type": "prompt"}
        simple_type = "select_one"
        row_type = row["type"]
        list_name = row_type.split(maxsplit=1)[1]

        try:
            if row_type.startswith("select_one_external "):
                choice_list = ext_choices[list_name]
            elif row_type.startswith("select_multiple_external "):
                simple_type = "select_multiple"
                choice_list = ext_choices[list_name]

            elif row_type.startswith("select_one "):
                choice_list = choices[list_name]
            elif row_type.startswith("select_multiple "):
                simple_type = "select_multiple"
                choice_list = choices[list_name]
            else:
                raise OdkFormError()
        except KeyError:
            raise OdkFormError("List '{}' not found.".format(list_name))

        simple_row["simple_type"] = simple_type
        simple_row["choice_list"] = choice_list

        appearance = row.get("appearance", "")
        if appearance in ("label", "list-nolabel"):
            simple_row["token_type"] = "table"

        return simple_row
Beispiel #3
0
def cli():
    """Command line interface for package.

    Side Effects: Executes program.

    Command Syntax: python3 -m ppp <file> <options>

    Examples:
        # Creates a 'myFile.html' in English with component highlighting.
        python3 -m ppp myFile.xlsx -l 'English' -h > myFile.html
    """
    prog_desc = 'Convert XLSForm to Paper version.'

    argeparser = ArgumentParser(description=prog_desc)
    parser = _add_arguments(copy(argeparser))
    args = parser.parse_args()

    if args.highlight and args.format and args.format not in SUPPORTED_FORMATS:
        msg = 'Can only specify highlighting when using the following ' \
              'formats: \'html\', \'pdf\'.'
        raise OdkFormError(msg)

    try:
        run(files=list(args.xlsxfiles),
            languages=[l for l in args.language] if args.language else [None],
            format=args.format,
            debug=args.debug,
            highlight=args.highlight,
            template=args.template,
            style=args.style,
            outpath=args.outpath)
    except OdkException as err:
        err = 'An error occurred while attempting to convert \'{}\':\n{}'\
            .format(args.xlsxfiles, err)
        print(err, file=stderr)
Beispiel #4
0
        def end_repeat(self):
            """Finish a repeat in this questionniare.

            A repeat can be ended only if it is first on the pending stack.

            Raises:
                OdkFormError: If the parsing rules are broken based on the
                    current context.

            """
            if self.pending_stack:
                last_pending = self.pending_stack.pop()
                if isinstance(last_pending, OdkRepeat):
                    self.result.append(last_pending)
                else:
                    msg = "Found end repeat but no repeat in pending stack."
                    raise OdkFormError(msg)
            else:
                msg = "Found end repeat but nothing in pending stack."
                raise OdkFormError(msg)
Beispiel #5
0
        def add_table(self, prompt):
            """Add a table row to the questionnaire.

            The table can only be added if there is a group on the pending
            stack.

            Args:
                prompt (OdkPrompt): The prompt representing the table row.

            Raises:
                OdkFormError: If the parsing rules are broken based on the
                    current context.

            """
            if self.pending_stack:
                last_pending = self.pending_stack[-1]
                if not isinstance(last_pending, OdkGroup):
                    msg = 'A table can only be in a group.'
                    raise OdkFormError(msg)
                last_pending.add_table(prompt)
            else:
                msg = 'A table can only be in a group, no group found.'
                raise OdkFormError(msg)
Beispiel #6
0
        def end_group(self):
            """Finish a group after seeing 'end group' type.

            The 'end group' type can finish a field-list group or a context
            group. This function handles the logic for finishing both types.

            Raises:
                OdkFormError: If the parsing rules are broken based on the
                    current context.

            """
            if self.group_stack:
                last_group = self.group_stack.pop()
                if isinstance(last_group, OdkGroup):
                    self.end_pending_group()
            else:
                msg = "Begin/end group mismatch"
                raise OdkFormError(msg)
Beispiel #7
0
        def add_repeat(self, repeat):
            """Add a repeat to the pending stack.

            The pending stack must first be empty because a repeat cannot be
            nested in a group or other repeat.

            Args:
                repeat (OdkRepeat): The repeat to deal with.

            Raises:
                OdkFormError: If the parsing rules are broken based on the
                    current context.

            """
            if not self.pending_stack:
                self.pending_stack.append(repeat)
            else:
                msg = "Unable to nest repeat inside a group or repeat."
                raise OdkFormError(msg)
Beispiel #8
0
        def add_group(self, group):
            """Add a group to the pending stack.

            A group can be added to the pending stack as long as it is empty
            or the last pending stack item is a repeat. This is triggered by a
            'begin group' row with a 'field-list' in the appearance.

            Args:
                group (OdkGroup): The group to add to the pending stack.

            Raises:
                OdkFormError: If the parsing rules are broken based on the
                    current context.

            """
            if self.pending_stack:
                last = self.pending_stack[-1]
                if isinstance(last, OdkGroup):
                    msg = "Groups cannot be nested in each other."
                    raise OdkFormError(msg)
            self.pending_stack.append(group)
            self.group_stack.append(group)
Beispiel #9
0
    def parse_group_repeat(row):
        """Extract relevant information about a begin/end group/repeat.

        Args:
            row (dict): A row as a dictionary. Keys and values are strings.

        Returns:
            A dictionary with the simple information about this prompt.

        Raises:
            OdkFormError: If type is not begin/end group/repeat.
        """
        row_type = row["type"]
        token_type = row_type
        appearance = row.get("appearance", "")
        good = ("begin group", "end group", "begin repeat", "end repeat")
        if row_type == "begin group" and "field-list" not in appearance:
            token_type = "context group"
        elif row_type not in good:
            raise OdkFormError()
        simple_row = {"token_type": token_type}
        return simple_row
Beispiel #10
0
    def get_choices(wb, ws):
        """Extract choices from an XLSForm.

        Args:
            wb (Xlsform): A Xlsform object representing ODK form.
            ws (Worksheet): One of 'choices' or 'external_choices'.

        Returns:
            dict: A dictionary of choice list names with list of choices
                options for each list.

        Raises:
            OdkformError: Catches instances where list specified in the
                'survey' worksheet, but the list does not appear in the
                designated 'choices' or 'external_choices' worksheet.
        """
        formatted_choices = {}
        try:
            choices = wb[ws]
            header = [str(x) for x in choices[0]]

            if "list_name" not in header:
                msg = 'Column "list_name" not found in {} tab'.format(ws)
                raise OdkFormError(msg)

            for i, row in enumerate(choices):
                if i == 0:
                    continue
                dict_row = {str(k): str(v) for k, v in zip(header, row)}
                list_name = dict_row["list_name"]
                if list_name in formatted_choices:
                    formatted_choices[list_name].add(dict_row)
                elif list_name:  # Not "else:" because possibly blank rows.
                    odkchoices = OdkChoices(list_name)
                    odkchoices.add(dict_row)
                    formatted_choices[list_name] = odkchoices
        except (KeyError, IndexError):  # Worksheet does not exist.
            pass
        return formatted_choices
Beispiel #11
0
    def parse_group_repeat(row):
        """Extract relevant information about a begin/end group/repeat.

        Args:
            row (dict): A row as a dictionary. Keys and values are strings.

        Returns:
            A dictionary with the simple information about this prompt.

        Raises:
            OdkFormError: If type is not begin/end group/repeat.
        """
        row_type = row['type']
        token_type = row_type
        appearance = row.get('appearance', '')
        good = ('begin group', 'end group', 'begin repeat', 'end repeat')
        if row_type == 'begin group' and 'field-list' not in appearance:
            token_type = 'context group'
        elif row_type not in good:
            raise OdkFormError()
        simple_row = {'token_type': token_type}
        return simple_row