Пример #1
0
    def page_processor(self, page, row):

        question = structures.ElementObject()

        # Check if an instruction or a question. Instrument fields not added
        # if a row's data_type is 'instruction'
        if row['data_type'] == 'instruction':
            question['type'] = 'text'
            question['options'] = {
                'text': localized_string_object(self.localization,
                                                row['text']),
            }
        else:
            question['type'] = 'question'
            question['options'] = structures.QuestionObject(
                fieldId=self.reader.get_name(row['fieldid']),
                text=localized_string_object(self.localization, row['text']),
                help=localized_string_object(self.localization, row['help']),
            )
            # Build field object
            field = structures.FieldObject(
                id=self.reader.get_name(row['fieldid']),
                description=row['text'],
                type=self._field_type,
            )

            # Process question and field
            self.question_and_field_processor(page, row, question, field)

            # Store instrument question field
            self._storage['i'] = field

        # Add the new question to form page
        page.add_element(question)
Пример #2
0
    def page_processor(self, page, row):

        section_header = row['section_header']
        if section_header:
            header = structures.ElementObject()
            header['type'] = 'header'
            header['options'] = {
                'text': localized_string_object(self.localization,
                                                section_header)
            }
        else:
            header = None

        # Check if a calc, and if so, remove, b/c not a form field/question
        if row['field_type'] == 'calc':
            question = None
        else:
            question = structures.ElementObject(type='question')
            field_name = self.reader.get_name(row['variable_field_name'])
            if section_header:
                field_name = '%s_%s' % (
                    field_name,
                    self.reader.get_name(section_header),
                )
            question['options'] = structures.QuestionObject(
                fieldId=field_name,
                text=localized_string_object(self.localization,
                                             row['field_label']),
                help=localized_string_object(self.localization,
                                             row['field_note']),
            )

        # Now that we have a header only OR question and maybe a header, we
        # either add the header by itself as a section header, or we add a new
        # question containing a possible sub-header. If no header or question,
        # then we have a calculation, which is handled when instrument fields
        # are constructed in the qestion_processor function.
        if header and not question:
            # Add non-questions to form (e.g., headers)
            page.add_element(header)

        # Process existing ques or process and add new ques to the form
        if question:
            add_question_and_store_fields = self.question_and_field_processor(
                page, row, question)

            # This block MUST COME AFTER the question processor to prevent
            # adding questions and fields that caused errors, but allow the
            # conversion to proceed to the next line.
            if add_question_and_store_fields:
                # Add the new question to form page
                page.add_element(question)
                # Store instrument question field
                if self._field and self._field['id']:
                    self._storage['i'] = self._field
Пример #3
0
 def process_yesno():
     if side_effects:
         question.set_widget(get_widget(type='radioGroup'))
         question.add_enumeration(
             structures.DescriptorObject(
                 id="yes",
                 text=localized_string_object(self.localization, "Yes"),
             ))
         question.add_enumeration(
             structures.DescriptorObject(
                 id="no",
                 text=localized_string_object(self.localization, "No"),
             ))
     type_object = structures.TypeObject(base='enumeration', )
     type_object.add_enumeration('yes', description='Yes')
     type_object.add_enumeration('no', description='No')
     return type_object
Пример #4
0
    def question_and_field_processor(self, page, row, question, field):
        """
        Processes questions and field types.

        Question object are fully configured, and the field type is generated
        for the instrument definition fields corresponding to the question.
        """

        # Use question object in question['options']
        question_obj = question['options']

        # Process choices before questions, so choices are available to
        # question processing
        if row['enumeration_type'] in (
                'enumeration',
                'enumerationSet',
        ):
            # data_type might be a JSON string of a dict which contains
            # 'Choices' or 'choices', an array of single key dicts.
            try:
                data_type = json.loads(row['data_type'])
                choices = (data_type.get('Choices', False)
                           or data_type.get('choices', False) or None)
            except:
                error = RedcapFormatError(
                    "Unable to parse \"data_type\" field:",
                    "Expected valid JSON formatted text")
                raise error

            # Sort the array on key order not array order
            if choices:
                field['type'] = structures.TypeObject(
                    base=row['enumeration_type'], )

                choices = [{
                    self.reader.get_name(k): v
                } for c in choices for k, v in c.items()]

                choices.sort()

                for choice in choices:
                    field['type'].add_enumeration(choice.keys()[0])
                    name, description = choice.items()[0]
                    question_obj.add_enumeration(
                        structures.DescriptorObject(
                            id=self.reader.get_name(name),
                            text=localized_string_object(
                                self.localization, description),
                        ))

                question_obj.set_widget(
                    structures.WidgetConfigurationObject(
                        type='checkGroup' if row['enumeration_type'] ==
                        'enumerationSet' else 'radioGroup'))

        else:
            field['type'] = row['data_type']
Пример #5
0
 def process_truefalse():
     if side_effects:
         question.set_widget(get_widget(type='radioGroup'))
         question.add_enumeration(
             structures.DescriptorObject(
                 id="true",
                 text=localized_string_object(self.localization,
                                              "True"),
             ))
         question.add_enumeration(
             structures.DescriptorObject(
                 id="false",
                 text=localized_string_object(self.localization,
                                              "False"),
             ))
     type_object = structures.TypeObject(base='enumeration', )
     type_object.add_enumeration('true', description='True')
     type_object.add_enumeration('false', description='False')
     return type_object
Пример #6
0
    def get_choices_form(self, row):
        """
        Returns array of DescriptorObject

        Expecting: choices_or_calculations to be pipe separated list
        of (comma delimited) tuples: internal, external
        """
        return [
            structures.DescriptorObject(
                id=self.reader.get_name(x.strip().split(',')[0]),
                text=localized_string_object(
                    self.localization,
                    ','.join(x.strip().split(',')[1:]).strip()),
            ) for x in row['choices_or_calculations'].split('|')
        ]
Пример #7
0
    def question_field_processor(self, question_data, question):
        """ Processe questions and fields """
        question_type = question_data['QuestionType']
        question_text = localized_string_object(
            self.localization,
            self.clean_question(question_data['QuestionText'])
        )
        if question_type == 'DB':
            # Question is only display text
            question['type'] = 'text'
            question['options'] = {'text': question_text}
        else:
            # Question is an interactive form element
            question['type'] = 'question'
            question['options'] = structures.QuestionObject(
                fieldId=question_data['DataExportTag'].lower(),
                text=localized_string_object(
                    self.localization,
                    self.clean_question(
                        question_data['QuestionText']
                    )
                ),
            )

            # Choices are generated, where "choices" is an array of
            # tuples: (id, choice)
            self._choices = question_data.get('Choices', [])
            order = question_data.get('ChoiceOrder', [])
            if self._choices:
                if isinstance(self._choices, dict):
                    if not order:
                        keys = self._choices.keys()
                        if all([k.isdigit() for k in keys]):
                            keys = [int(k) for k in keys]
                        order = sorted(keys)
                    self._choices = [(x, self._choices[str(x)]) for x in order]
                elif isinstance(self._choices, list):
                    self._choices = [i for i in enumerate(self._choices)]
                else:
                    error = ConversionValueError(
                        "Choices are not formatted correctly. Got choices:",
                        str(self._choices)
                    )
                    error.wrap("With question data:", str(question))
                    raise error
                self._choices = [
                    (str(i).lower(), c['Display'])
                    for i, c in self._choices
                ]
                # Process question object and field type object
                question_obj = question['options']
                field_type = structures.TypeObject(base='enumeration', )
                for _id, choice in self._choices:
                    question_obj.add_enumeration(
                        structures.DescriptorObject(
                            id=_id,
                            text=localized_string_object(
                                self.localization,
                                choice
                            ),
                        )
                    )
                    field_type.add_enumeration(str(_id))
            else:
                field_type = 'text'

            # Consruct field for instrument definition
            field = structures.FieldObject(
                id=question_data['DataExportTag'].lower(),
                description=question_data['QuestionDescription'],
                type=field_type,
                required=False,
                identifiable=False,
            )
            self._field_storage.append(field)
Пример #8
0
    def question_and_field_processor(self, page, row, question):
        """
        Processes questions.

        Return True if a question should be added as a new element to the page,
        and if a form field should be added to the instrument definition. New
        matrix questions and regular questions are added to both definitions.
        However, existing matrix questions are not added.

        A pointer reference is store to allow for:
            1) modifying existing matrix questions
            2) storing the instrument field by the parent function scope
        """

        add_question_and_store_field = True

        # Use question object in question['options']
        question_obj = question['options']

        # If row involves branching logic, add to question
        if row['branching_logic']:
            question_obj.add_event(
                structures.EventObject(
                    trigger=self.convert_trigger(row['branching_logic']),
                    action='disable',
                ))

        # Check for a matrix question
        matrix_group_name = self.reader.get_name(
            row.get('matrix_group_name', ''))

        if matrix_group_name:
            # Question is a matrix question
            #
            # assessment[m][r][c]
            # m = matrix_group_name
            # r = variable_field_name
            # c = field_type

            # Ranme for code clarity
            matrix = question_obj

            if self._current_matrix_group_name != matrix_group_name:
                # New matrix question

                self._current_matrix_group_name = matrix_group_name

                # Start a new matrix question field for the form
                matrix['fieldId'] = matrix_group_name

                # Create a new matrix question field for the instrument
                field = structures.FieldObject()
                field['id'] = self.reader.get_name(row['matrix_group_name'])
                field['description'] = row.get('section_header', '')
                field['type'] = structures.TypeObject(base='matrix', )

                field_type = field['type']

                # Process matrix
                # Append the only column(to instrument).
                # Use the field_type (checkbox or radiobutton) as the id.
                field_type.add_column(
                    structures.ColumnObject(
                        id=self.reader.get_name(row['field_type']),
                        description=row['field_type'],
                        type=self.get_type(matrix, row, side_effects=False),
                        required=bool(row['required_field']),
                        identifiable=bool(row['identifier']),
                    ))
                # add the column to the form
                matrix.add_question(
                    structures.QuestionObject(
                        fieldId=self.reader.get_name(row['field_type']),
                        text=localized_string_object(self.localization,
                                                     row['field_label']),
                        enumerations=self.get_choices_form(row),
                    ))
                # Append the first row (to instrument).
                field_type.add_row(
                    structures.RowObject(
                        id=self.reader.get_name(row['variable_field_name']),
                        description=row['field_label'],
                        required=bool(row['required_field']),
                    ))
                # add the row to the form.
                matrix.add_row(
                    structures.DescriptorObject(
                        id=self.reader.get_name(row['variable_field_name']),
                        text=localized_string_object(self.localization,
                                                     row['field_label']),
                    ))

                # Assign pointers to matrix construction objects, so
                # matrix questions have modifiable instrument and form
                # definitions if next question is  an existing matrix
                # question
                self._matrix = matrix
                self._field = field
                self._field_type = field_type

            else:
                # Current matrix question
                # Modify existing matrix question for instrument definition
                self._field_type.add_row(
                    structures.RowObject(
                        id=self.reader.get_name(row['variable_field_name']),
                        description=row['field_label'],
                        required=bool(row['required_field']),
                    ))

                # Field already exists, so prevent adding it to form
                self._field = structures.FieldObject()

                # Modify existing matrix question for form definition
                self._matrix.add_row(
                    structures.DescriptorObject(
                        id=self.reader.get_name(row['variable_field_name']),
                        text=localized_string_object(self.localization,
                                                     row['field_label']),
                    ))
                self._field = None
                add_question_and_store_field = False
        else:
            # A non-matrix, regular question
            self._current_matrix_group_name = None
            self._matrix = None
            self._field_type = None

            # Make instrument field
            field = structures.FieldObject()
            field_type = self.get_type(question_obj, row)
            if field_type:
                field_name = self.reader.get_name(row['variable_field_name'])
                if row['section_header']:
                    field_name = '%s_%s' % (
                        field_name,
                        self.reader.get_name(row['section_header']),
                    )
                field['id'] = field_name
                field['description'] = row['field_label']
                field['type'] = field_type
                field['required'] = bool(row['required_field'])
                field['identifiable'] = bool(row['identifier'])

            self._field = field

        return add_question_and_store_field