def test_previous_with_conditional_path(self):
        survey = load_schema_file("0_star_wars.json")

        expected_path = [
            "f22b1ba4-d15f-48b8-a1f3-db62b6f34cc0",
            "923ccc84-9d47-4a02-8ebc-1e9d14fcf10b",
            "26f2c4b3-28ac-4072-9f18-a6a6c6f660db",
            "cd3b74d1-b687-4051-9634-a8f9ce10a27d",
            "an3b74d1-b687-4051-9634-a8f9ce10ard",
            "846f8514-fed2-4bd7-8fb2-4b5fcb1622b1"
        ]

        answers = {
            "ca3ce3a3-ae44-4e30-8f85-5b6a7a2fb23c": "Dark Side",
            "pel989b8-5185-4ba6-b73f-c126e3a06ba7": "Can I be a pain and have a goodies ship"
        }

        current_block_id = expected_path[3]
        expected_previous_block_id = expected_path[2]

        navigator = Navigator(survey)
        actual_previous_block_id = navigator.get_previous_location(answers, current_block_id)

        self.assertEqual(actual_previous_block_id, expected_previous_block_id)

        current_block_id = expected_path[2]
        expected_previous_block_id = expected_path[1]

        actual_previous_block_id = navigator.get_previous_location(answers, current_block_id)

        self.assertEqual(actual_previous_block_id, expected_previous_block_id)
    def test_first_block(self):
        survey = load_schema_file("1_0102.json")

        first_block_id = "5bce8d8f-0af8-4d35-b77d-744e6179b406"

        navigator = Navigator(survey)
        self.assertEqual(navigator.get_first_block_id(), first_block_id)
    def test_next_location_empty_routing_rules(self):
        survey = load_schema_file("test_checkbox.json")
        navigator = Navigator(survey)

        # Force some empty routing rules
        navigator.blocks[0]['routing_rules'] = []

        expected_path = [
          'introduction',
          'f22b1ba4-d15f-48b8-a1f3-db62b6f34cc0',
          'f22b1ba4-d15f-48b8-a1f3-db62b6f34cc1',
          'summary'
        ]

        answers = {
          "ca3ce3a3-ae44-4e30-8f85-5b6a7a2fb23c": "Cheese",
          "ca3ce3a3-ae44-4e30-8f85-5b6a7a2fb23": "deep pan",
        }

        current_location_id = expected_path[1]
        expected_next_location_id = expected_path[2]

        next_location_id = navigator.get_next_location(answers, current_location_id)

        self.assertEqual(next_location_id, expected_next_location_id)
    def test_next_with_conditional_path(self):
        survey = load_schema_file("0_star_wars.json")

        expected_path = [
            "f22b1ba4-d15f-48b8-a1f3-db62b6f34cc0",
            "96682325-47ab-41e4-a56e-8315a19ffe2a",
            "cd3b74d1-b687-4051-9634-a8f9ce10a27d",
            "an3b74d1-b687-4051-9634-a8f9ce10ard",
            "846f8514-fed2-4bd7-8fb2-4b5fcb1622b1"
        ]

        answers = {
            "ca3ce3a3-ae44-4e30-8f85-5b6a7a2fb23c": "Light Side",
            "2e0989b8-5185-4ba6-b73f-c126e3a06ba7": "No"
        }

        current_block_id = expected_path[1]
        expected_next_block_id = expected_path[2]

        navigator = Navigator(survey)
        actual_next_block_id = navigator.get_next_location(answers, current_block_id)

        self.assertEqual(actual_next_block_id, expected_next_block_id)

        current_block_id = expected_path[2]
        expected_next_block_id = expected_path[3]
        actual_next_block_id = navigator.get_next_location(answers, current_block_id)

        self.assertEqual(actual_next_block_id, expected_next_block_id)
    def test_next_block(self):
        survey = load_schema_file("1_0102.json")

        current_block_id = "7418732e-12fb-4270-8307-5682ac63bfae"
        next_block_id = "02ed26ad-4cfc-4e29-a946-630476228b2c"

        navigator = Navigator(survey)
        self.assertEqual(navigator.get_next_location(current_location_id=current_block_id), next_block_id)
    def test_get_previous_location(self):
        survey = load_schema_file("0_star_wars.json")

        navigator = Navigator(survey)

        next_location = navigator.get_previous_location(current_location_id='f22b1ba4-d15f-48b8-a1f3-db62b6f34cc0')

        self.assertEqual('introduction', next_location)
    def test_interstitial_post_blocks(self):
        survey = load_schema_file("0_star_wars.json")
        navigator = Navigator(survey)

        answers = {
            "ca3ce3a3-ae44-4e30-8f85-5b6a7a2fb23c": "Light Side"
        }

        self.assertFalse('summary' in navigator.get_location_path(answers))
    def test_routing_basic_path(self):
        survey = load_schema_file("1_0112.json")
        expected_path = [
            "980b148e-0856-4e50-9afe-67a4fa6ae13b",
            "6c8a2f39-e0d8-406f-b463-2151225abea2",
            "0c7c8876-6a63-4251-ac29-b821b3e9b1bc",
            "a42b5752-1896-4f52-9d58-320085be92a7",
            "0b29d3f7-5905-43d8-9921-5b353db68104",
            "7e2d49eb-ffc7-4a61-a45d-eba336d1d0e6",
        ]

        navigator = Navigator(survey)
        routing_path = navigator.get_routing_path()

        self.assertEqual(routing_path, expected_path)
    def test_next_location_goto_summary(self):
        survey = load_schema_file("0_star_wars.json")
        navigator = Navigator(survey)

        expected_path = [
            'introduction',
            'f22b1ba4-d15f-48b8-a1f3-db62b6f34cc0',
            'summary'
        ]

        answers = {
            "ca3ce3a3-ae44-4e30-8f85-5b6a7a2fb23c": "I prefer Star Trek"
        }

        current_location_id = expected_path[1]
        expected_next_location_id = expected_path[2]

        next_location_id = navigator.get_next_location(answers, current_location_id)

        self.assertEqual(next_location_id, expected_next_location_id)
    def test_routing_conditional_path(self):
        survey = load_schema_file("0_star_wars.json")

        expected_path = [
            "f22b1ba4-d15f-48b8-a1f3-db62b6f34cc0",
            "96682325-47ab-41e4-a56e-8315a19ffe2a",
            "cd3b74d1-b687-4051-9634-a8f9ce10a27d",
            "an3b74d1-b687-4051-9634-a8f9ce10ard",
            "846f8514-fed2-4bd7-8fb2-4b5fcb1622b1"
        ]

        answers = {
            "ca3ce3a3-ae44-4e30-8f85-5b6a7a2fb23c": "Light Side",
            "2e0989b8-5185-4ba6-b73f-c126e3a06ba7": "No"
        }

        navigator = Navigator(survey)
        routing_path = navigator.get_routing_path(answers)

        self.assertEqual(routing_path, expected_path)
class TestConfirmationPage(unittest.TestCase):
    def setUp(self):
        survey = load_schema_file("0_rogue_one.json")

        self.navigator = Navigator(survey)

    def test_get_next_location_confirmation(self):
        current_location_id = "5e633f04-073a-44c0-a9da-a7cc2116b937"
        answers = {"ca3ce3a3-ae44-4e30-8f85-5b6a7a2fb23c": "Orson Krennic"}
        next_location = self.navigator.get_next_location(answers, current_location_id)
        self.assertEqual("summary", next_location)
    def test_get_next_location_summary(self):
        survey = load_schema_file("0_star_wars.json")

        navigator = Navigator(survey)

        answers = {
            "ca3ce3a3-ae44-4e30-8f85-5b6a7a2fb23c": "Light Side",
            "2e0989b8-5185-4ba6-b73f-c126e3a06ba7": "No"
        }

        current_location_id = 'an3b74d1-b687-4051-9634-a8f9ce10ard'
        next_location_id = navigator.get_next_location(answers, current_location_id)
        expected_next_location_id = '846f8514-fed2-4bd7-8fb2-4b5fcb1622b1'

        self.assertEqual(expected_next_location_id, next_location_id)

        current_location_id = '846f8514-fed2-4bd7-8fb2-4b5fcb1622b1'
        next_location_id = navigator.get_next_location(answers, current_location_id)
        expected_next_location_id = 'summary'

        self.assertEqual(expected_next_location_id, next_location_id)
    def __init__(self, schema, json=None):
        self._json = json
        self._schema = schema
        self.state = None

        self.navigator = Navigator(self._json)
class QuestionnaireManager(object):
    '''
    This class represents a user journey through a survey. It models the request/response process of the web application
    '''
    def __init__(self, schema, json=None):
        self._json = json
        self._schema = schema
        self.state = None

        self.navigator = Navigator(self._json)

    def validate(self, location, post_data):

        answers = get_answers(current_user)

        if location in self.navigator.get_location_path(answers):

            self.build_state(location, post_data)

            if self.state:
                self._conditional_display(self.state)
                is_valid = self.state.schema_item.validate(self.state)
                # Todo, this doesn't feel right, validation is casting the user values to their type.

                return is_valid

            else:
                # Item has node, but is not in schema: must be introduction, thank you or summary
                return True
        else:
            # Not a validation location, so can't be valid
            return False

    def validate_all_answers(self):

        answers = get_answers(current_user)

        for location in self.navigator.get_location_path(answers):
            is_valid = self.validate(location, get_answers(current_user))

            if not is_valid:
                logger.debug("Failed validation with current location %s", location)
                return False, location

        return True, None

    def process_incoming_answers(self, location, post_data):
        logger.debug("Processing post data for %s", location)

        is_valid = self.validate(location, post_data)
        # run the validator to update the validation_store
        if is_valid:

            # Store answers in QuestionnaireStore
            questionnaire_store = get_questionnaire_store(current_user.user_id, current_user.user_ik)

            for answer in self.get_state_answers(location):
                questionnaire_store.answers[answer.id] = answer.value

            if location not in questionnaire_store.completed_blocks:
                questionnaire_store.completed_blocks.append(location)

            questionnaire_store.save()

        return is_valid

    def get_rendering_context(self, location, is_valid=True):

        if is_valid:
            if location == 'summary':
                return self.get_summary_rendering_context()
            else:
                # apply page answers?
                self.build_state(location, get_answers(current_user))

        if self.state:
            self._plumbing_preprocessing()
            self._conditional_display(self.state)

        # look up the preprocessor and then build the view data
        return build_questionnaire_model(self._json, self.state)

    def get_summary_rendering_context(self):
        schema_template_context = get_schema_template_context(self, self._schema)
        rendered_questionnaire_schema_json = render_template_string(json.dumps(self._json), **schema_template_context)

        # look up the preprocessor and then build the view data
        return build_summary_model(json.loads(rendered_questionnaire_schema_json))

    def build_state(self, item_id, answers):
        # Build the state from the answers
        self.state = None
        if self._schema.item_exists(item_id):
            schema_item = self._schema.get_item_by_id(item_id)
            self.state = schema_item.construct_state()
            self.state.update_state(answers)

    def get_state_answers(self, item_id):
        # get the answers from the state
        if self._schema.item_exists(item_id):
            return self.state.get_answers()

        return []

    def _plumbing_preprocessing(self):
        # Run the current state through the plumbing preprocessor
        plumbing_template_preprocessor = PlumbingPreprocessor()
        plumbing_template_preprocessor.plumb_current_state(self, self.state, self._schema)

    def _conditional_display(self, item):
        # Process any conditional display rules

        if item.schema_item:

            item.skipped = False

            if hasattr(item.schema_item, 'skip_condition') and item.schema_item.skip_condition:
                rule = item.schema_item.skip_condition.__dict__
                rule['when'] = rule['when'].__dict__
                answer = get_answers(current_user).get(rule['when']['id'])

                item.skipped = evaluate_rule(rule, answer)

            for child in item.children:
                self._conditional_display(child)

    def get_schema_item_by_id(self, item_id):
        return self._schema.get_item_by_id(item_id)

    def get_schema(self):
        return self._schema
    def setUp(self):
        survey = load_schema_file("0_rogue_one.json")

        self.navigator = Navigator(survey)