def routing_path(
        self, section_id: str, list_item_id: Optional[str] = None
    ) -> RoutingPath:
        """
        Visits all the blocks in a section and returns a path given a list of answers.
        """
        blocks: List[Mapping] = []
        routing_path_block_ids = []
        current_location = Location(section_id=section_id, list_item_id=list_item_id)
        section = self.schema.get_section(section_id)
        list_name = self.schema.get_repeating_list_for_section(
            current_location.section_id
        )

        for group in section["groups"]:
            if "skip_conditions" in group:
                if evaluate_skip_conditions(
                    group["skip_conditions"],
                    self.schema,
                    self.metadata,
                    self.answer_store,
                    self.list_store,
                    current_location=current_location,
                ):
                    continue

            blocks.extend(group["blocks"])

        if blocks:
            routing_path_block_ids = self._build_routing_path_block_ids(
                blocks, current_location
            )

        return RoutingPath(routing_path_block_ids, section_id, list_item_id, list_name)
    def test_evaluate_skip_condition_returns_true_when_more_than_one_rule_is_true(self):
        # Given
        skip_conditions = [
            {
                'when': [
                    {
                        'id': 'this',
                        'condition': 'equals',
                        'value': 'value'
                    }
                ]
            },
            {
                'when': [
                    {
                        'id': 'that',
                        'condition': 'equals',
                        'value': 'other value'
                    }
                ]
            }
        ]
        answer_store = AnswerStore({})
        answer_store.add(Answer(answer_id='this', value='value'))
        answer_store.add(Answer(answer_id='that', value='other value'))

        # When
        condition = evaluate_skip_conditions(skip_conditions, get_schema_mock(), {}, answer_store)

        # Then
        self.assertTrue(condition)
    def test_evaluate_skip_condition_returns_false_when_both_or_rules_false(self):
        # Given
        skip_conditions = [
            {
                'when': [
                    {
                        'id': 'this',
                        'condition': 'equals',
                        'value': 'value'
                    }
                ]
            },
            {
                'when': [
                    {
                        'id': 'that',
                        'condition': 'equals',
                        'value': 'other value'
                    }
                ]
            }
        ]
        answer_store = AnswerStore({})
        answer_store.add(Answer(answer_id='this', value='not correct'))
        answer_store.add(Answer(answer_id='that', value='not correct'))

        # When
        condition = evaluate_skip_conditions(skip_conditions, get_schema_mock(), {}, answer_store)

        # Then
        self.assertFalse(condition)
    def _build_routing_path_block_ids(self, blocks, current_location):
        # Keep going unless we've hit the last block
        routing_path_block_ids = []
        block_index = 0
        repeating_list = self.schema.get_repeating_list_for_section(
            current_location.section_id
        )

        while block_index < len(blocks):
            block = blocks[block_index]

            is_skipping = block.get("skip_conditions") and evaluate_skip_conditions(
                block["skip_conditions"],
                self.schema,
                self.metadata,
                self.answer_store,
                self.list_store,
                current_location=current_location,
                routing_path_block_ids=routing_path_block_ids,
            )

            if not is_skipping:
                block_id = block["id"]
                if repeating_list and current_location.list_item_id:
                    this_location = Location(
                        section_id=current_location.section_id,
                        block_id=block_id,
                        list_name=repeating_list,
                        list_item_id=current_location.list_item_id,
                    )
                else:
                    this_location = Location(
                        section_id=current_location.section_id, block_id=block_id
                    )

                if block_id not in routing_path_block_ids:
                    routing_path_block_ids.append(block_id)

                # If routing rules exist then a rule must match (i.e. default goto)
                routing_rules = block.get("routing_rules")
                if routing_rules:
                    block_index = self._evaluate_routing_rules(
                        this_location,
                        blocks,
                        routing_rules,
                        block_index,
                        routing_path_block_ids,
                    )
                    if block_index:
                        continue

                    return routing_path_block_ids

            # Last block so return routing_path_block_ids
            if block_index == len(blocks) - 1:
                return routing_path_block_ids

            # No routing rules, so step forward a block
            block_index = block_index + 1
    def test_evaluate_skip_condition_returns_false_when_no_skip_condition(self):
        # Given
        skip_conditions = None

        # When
        condition = evaluate_skip_conditions(skip_conditions, get_schema_mock(), {}, AnswerStore({}))

        # Then
        self.assertFalse(condition)
Exemplo n.º 6
0
    def build_path(self):
        """
        Visits all the blocks from a location forwards and returns path
        taken given a list of answers.

        :param blocks: A list containing all block content in the survey
        :param this_location: The location to visit, represented as a dict
        :return: A list of locations followed through the survey
        """
        this_location = None

        blocks = []
        path = []
        block_index = 0
        first_groups = self._get_first_group_in_section()

        for group in self.schema.groups:
            first_block_in_group = self.schema.get_first_block_id_for_group(
                group['id'])
            if not this_location:
                this_location = Location(group['id'], 0, first_block_in_group)

            if 'skip_conditions' in group:
                if evaluate_skip_conditions(group['skip_conditions'],
                                            self.schema, self.metadata,
                                            self.answer_store):
                    continue

            no_of_repeats = get_number_of_repeats(group, self.schema, path,
                                                  self.answer_store)

            first_group_instance_index = None
            for instance_idx in range(0, no_of_repeats):
                group_blocks = list(
                    self._build_blocks_for_group(group, instance_idx))
                blocks += group_blocks

                if group_blocks and first_group_instance_index is None:
                    # get the group instance of the first instance of a block in this group that has not been skipped
                    first_group_instance_index = group_blocks[0][
                        'group_instance']

            all_blocks_skipped = first_group_instance_index is None

            if not all_blocks_skipped:
                if group['id'] in first_groups:
                    this_location = Location(group['id'],
                                             first_group_instance_index,
                                             first_block_in_group)

                if blocks:
                    path, block_index = self._build_path_within_group(
                        blocks, block_index, this_location, path)

        return RoutingPath(path)
 def _build_questions(block_schema, answer_store, metadata, schema):
     questions = []
     for question_schema in block_schema.get('questions', []):
         is_skipped = evaluate_skip_conditions(
             question_schema.get('skip_conditions'), schema, metadata,
             answer_store)
         if not is_skipped:
             question = Question(question_schema, answer_store, metadata,
                                 schema).serialize()
             questions.append(question)
     return questions
Exemplo n.º 8
0
def _evaluate_skip_conditions(block_json, location, schema, answer_store,
                              metadata):
    for question in schema.get_questions_for_block(block_json):
        if 'skip_conditions' in question:
            skip_question = evaluate_skip_conditions(
                question['skip_conditions'], schema, metadata, answer_store,
                location.group_instance)
            question['skipped'] = skip_question
            for answer in question['answers']:
                if answer['mandatory'] and skip_question:
                    answer['mandatory'] = False

    return block_json
Exemplo n.º 9
0
    def _build_blocks_for_group(self, group, instance_idx):
        for block in group['blocks']:
            skip_conditions = block.get('skip_conditions')
            if skip_conditions and evaluate_skip_conditions(
                    skip_conditions, self.schema, self.metadata,
                    self.answer_store, instance_idx):
                continue

            yield {
                'group_id': group['id'],
                'group_instance': instance_idx,
                'block': block,
            }
    def _build_path_within_group(self, blocks, block_index, this_location,
                                 path):
        # Keep going unless we've hit the last block
        # for block_identifier in blocks:
        while block_index < len(blocks):
            prev_block_index = block_index
            block_index = PathFinder._block_index_for_location(
                blocks, this_location)
            if block_index is None:
                return path, prev_block_index

            block = blocks[block_index]['block']

            if block.get('skip_conditions') and \
                    evaluate_skip_conditions(block['skip_conditions'], self.schema, self.metadata,
                                             self.answer_store, this_location.group_instance, routing_path=path):

                if block_index == len(blocks) - 1:
                    return path, block_index

                this_location = Location(
                    blocks[block_index + 1]['group_id'],
                    blocks[block_index + 1]['group_instance'],
                    blocks[block_index + 1]['block']['id'])
                continue

            if this_location not in path:
                path.append(this_location)

            # If routing rules exist then a rule must match (i.e. default goto)
            if 'routing_rules' in block and block['routing_rules']:
                this_location = self._evaluate_routing_rules(
                    this_location, blocks, block, block_index, path)

                if this_location:
                    continue

                return path, block_index

            # No routing rules, so if this isn't the last block, step forward a block
            if block_index < len(blocks) - 1:
                this_location = Location(
                    blocks[block_index + 1]['group_id'],
                    blocks[block_index + 1]['group_instance'],
                    blocks[block_index + 1]['block']['id'])
                continue

            return path, block_index
Exemplo n.º 11
0
    def test_evaluate_skip_condition_returns_false_when_no_skip_condition(self):
        # Given
        skip_conditions = None

        current_location = Location(section_id="some-section", block_id="some-block")

        # When
        condition = evaluate_skip_conditions(
            skip_conditions=skip_conditions,
            schema=get_schema(),
            metadata={},
            answer_store=AnswerStore(),
            list_store=ListStore(),
            current_location=current_location,
        )

        # Then
        self.assertFalse(condition)
Exemplo n.º 12
0
    def test_evaluate_skip_condition_returns_false_when_both_or_rules_false(
            self):
        # Given
        skip_conditions = [
            {
                "when": [{
                    "id": "this",
                    "condition": "equals",
                    "value": "value"
                }]
            },
            {
                "when": [{
                    "id": "that",
                    "condition": "equals",
                    "value": "other value"
                }]
            },
        ]
        answer_store = AnswerStore()
        answer_store.add_or_update(
            Answer(answer_id="this", value="not correct"))
        answer_store.add_or_update(
            Answer(answer_id="that", value="not correct"))

        current_location = Location(section_id="some-section",
                                    block_id="some-block")

        # When
        condition = evaluate_skip_conditions(
            skip_conditions=skip_conditions,
            schema=get_schema(),
            metadata={},
            answer_store=answer_store,
            list_store=ListStore(),
            current_location=current_location,
        )

        # Then
        self.assertFalse(condition)
Exemplo n.º 13
0
    def test_evaluate_skip_condition_returns_true_when_that_rule_true(self):

        skip_conditions = [{
            'when': [{
                'id': 'this',
                'condition': 'equals',
                'value': 'value'
            }]
        }, {
            'when': [{
                'id': 'that',
                'condition': 'equals',
                'value': 'other value'
            }]
        }]
        answer_store = AnswerStore({})

        answer_store.add_or_update(
            Answer(answer_id='that', value='other value'))

        self.assertTrue(
            evaluate_skip_conditions(skip_conditions, get_schema_mock(), {},
                                     answer_store))
Exemplo n.º 14
0
    def test_evaluate_skip_condition_returns_true_when_that_rule_true(self):

        skip_conditions = [
            {"when": [{"id": "this", "condition": "equals", "value": "value"}]},
            {"when": [{"id": "that", "condition": "equals", "value": "other value"}]},
        ]
        answer_store = AnswerStore()

        answer_store.add_or_update(Answer(answer_id="that", value="other value"))

        current_location = Location(section_id="some-section", block_id="some-block")

        # When
        self.assertTrue(
            evaluate_skip_conditions(
                skip_conditions=skip_conditions,
                schema=get_schema(),
                metadata={},
                answer_store=answer_store,
                list_store=ListStore(),
                current_location=current_location,
            )
        )
Exemplo n.º 15
0
 def _should_skip(self, group_or_block):
     return ('skip_conditions' in group_or_block
             and evaluate_skip_conditions(group_or_block['skip_conditions'],
                                          self.schema, self.metadata,
                                          self.answer_store))