def test_answer_source_count(rule, expected_result): rule_evaluator = get_rule_evaluator(answer_store=AnswerStore([{ "answer_id": "some-answer", "value": ["array element 1", "array element 2"], }]), ) assert rule_evaluator.evaluate(rule=rule) is expected_result
def test_answer_source_not_on_path_non_repeating_section(is_answer_on_path): schema = get_mock_schema() location = Location(section_id="test-section", block_id="test-block") if is_answer_on_path: schema.get_block_for_answer_id = Mock( return_value={"id": "block-on-path"}) answer_id = "answer-on-path" expected_result = True else: schema.get_block_for_answer_id = Mock( return_value={"id": "block-not-on-path"}) answer_id = "answer-not-on-path" expected_result = False answer = Answer(answer_id=answer_id, value="Yes") rule_evaluator = get_rule_evaluator( schema=schema, answer_store=AnswerStore([answer.to_dict()]), location=location, routing_path_block_ids=["block-on-path"], ) assert (rule_evaluator.evaluate( rule={ Operator.EQUAL: [ "Yes", { "source": "answers", "identifier": "answer-on-path" }, ] }) == expected_result)
def get_rule_evaluator( *, language="en", schema: QuestionnaireSchema = None, answer_store: AnswerStore = AnswerStore(), list_store: ListStore = ListStore(), metadata: Optional[dict] = None, response_metadata: Mapping = None, location: Union[Location, RelationshipLocation] = Location(section_id="test-section", block_id="test-block"), routing_path_block_ids: Optional[list] = None, ): if not schema: schema = get_mock_schema() schema.is_repeating_answer = Mock(return_value=True) schema.get_default_answer = Mock(return_value=None) return RuleEvaluator( language=language, schema=schema, metadata=metadata or {}, response_metadata=response_metadata, answer_store=answer_store, list_store=list_store, location=location, routing_path_block_ids=routing_path_block_ids, )
def _get_checkbox_answer_data(answer_store: AnswerStore, answer_schema: Mapping, value: ListAnswer) -> dict[str, str]: qcodes_and_values = [] for user_answer in value: # find the option in the schema which matches the users answer option = next( (option for option in answer_schema["options"] if option["value"] == user_answer), None, ) if option: if "detail_answer" in option: detail_answer = answer_store.get_answer( option["detail_answer"]["id"]) # if the user has selected an option with a detail answer we need to find the detail answer value it refers to. # the detail answer value can be empty, in this case we just use the main value (e.g. other) user_answer = detail_answer.value or user_answer # type: ignore qcodes_and_values.append((option.get("q_code"), user_answer)) checkbox_answer_data: dict[str, str] = OrderedDict() if all(q_code is not None for (q_code, _) in qcodes_and_values): checkbox_answer_data.update(qcodes_and_values) else: checkbox_answer_data[answer_schema["q_code"]] = str( [v for (_, v) in qcodes_and_values]) return checkbox_answer_data
def test_nested_rules(operator, operands, expected_result): rule_evaluator = get_rule_evaluator( answer_store=AnswerStore([ { "answer_id": "answer-1", "list_item_id": "item-1", "value": "Yes, I do", }, { "answer_id": "answer-2", "list_item_id": "item-1", "value": 10, }, ]), metadata={ "region_code": "GB-NIR", "language_code": "en" }, list_store=ListStore([{ "name": "some-list", "items": get_list_items(5), "same_name_items": get_list_items(3), }], ), location=Location(section_id="some-section", block_id="some-block", list_item_id="item-1"), ) assert rule_evaluator.evaluate( rule={operator: operands}) is expected_result
def test_answer_source_with_list_item_selector_list_first_item( answer_value, expected_result): rule_evaluator = get_rule_evaluator( answer_store=AnswerStore([{ "answer_id": "some-answer", "list_item_id": "item-1", "value": answer_value, }]), list_store=ListStore([{ "name": "some-list", "items": get_list_items(3) }]), ) assert (rule_evaluator.evaluate( rule={ Operator.EQUAL: [ { "source": "answers", "identifier": "some-answer", "list_item_selector": { "source": "list", "identifier": "some-list", "selector": "first", }, }, 3, ] }) is expected_result)
def test_answer_source_with_routing_path_block_ids_outside_repeat( is_answer_on_path): schema = get_mock_schema() location = Location(section_id="test-section", block_id="test-block") if is_answer_on_path: schema.get_block_for_answer_id = Mock( return_value={"id": f"block-on-path"}) answer_id = "answer-on-path" expected_result = "Yes" else: schema.get_block_for_answer_id = Mock( return_value={"id": f"block-not-on-path"}) answer_id = "answer-not-on-path" expected_result = None answer = Answer(answer_id=answer_id, value="Yes") value_source_resolver = get_value_source_resolver( schema=schema, answer_store=AnswerStore([answer.to_dict()]), list_store=ListStore([{ "name": "some-list", "items": get_list_items(3) }]), location=location, list_item_id=location.list_item_id, routing_path_block_ids=["block-on-path"], ) assert (value_source_resolver.resolve({ "source": "answers", "identifier": "answer-on-path" }) == expected_result)
def get_value_source_resolver( schema: QuestionnaireSchema = None, answer_store: AnswerStore = AnswerStore(), list_store: ListStore = ListStore(), metadata: Optional[dict] = None, location: Union[Location, RelationshipLocation] = Location(section_id="test-section", block_id="test-block"), list_item_id: Optional[str] = None, routing_path_block_ids: Optional[list] = None, use_default_answer=False, escape_answer_values=False, ): if not schema: schema = get_mock_schema() schema.is_repeating_answer = Mock(return_value=bool(list_item_id)) if not use_default_answer: schema.get_default_answer = Mock(return_value=None) return ValueSourceResolver( answer_store=answer_store, list_store=list_store, metadata=metadata, schema=schema, location=location, list_item_id=list_item_id, routing_path_block_ids=routing_path_block_ids, use_default_answer=use_default_answer, escape_answer_values=escape_answer_values, )
def test_answer_source_with_list_item_selector_location( answer_value, expected_result): rule_evaluator = get_rule_evaluator( answer_store=AnswerStore([{ "answer_id": "some-answer", "list_item_id": "item-1", "value": answer_value, }]), location=Location(section_id="some-section", block_id="some-block", list_item_id="item-1"), ) assert (rule_evaluator.evaluate(rule={ Operator.EQUAL: [ { "source": "answers", "identifier": "some-answer", "list_item_selector": { "source": "location", "identifier": "list_item_id", }, }, 3, ] }, ) is expected_result)
def test_map_with_nested_date_operator(offset, expected_result): rule = { Operator.MAP: [ { Operator.FORMAT_DATE: [ { Operator.DATE: [ "self", offset, ] }, "d MMMM yyyy", ] }, { "source": "answers", "identifier": "checkbox-answer" }, ] } rule_evaluator = get_rule_evaluator(answer_store=AnswerStore([{ "answer_id": "checkbox-answer", "value": ["2021-01-01", "2021-01-02", "2021-01-03"], }])) assert rule_evaluator.evaluate(rule=rule) == expected_result
def test_format_date(rule, expected_result): rule_evaluator = get_rule_evaluator(answer_store=AnswerStore([{ "answer_id": "some-answer", "value": "2021-01-01", }]), ) assert rule_evaluator.evaluate(rule=rule) == expected_result
def test_date_value(rule, expected_result): rule_evaluator = get_rule_evaluator( answer_store=AnswerStore([{ "answer_id": "some-answer", "value": current_date_as_yyyy_mm_dd, }]), metadata={"some-metadata": current_date_as_yyyy_mm_dd}, ) assert (rule_evaluator.evaluate(rule=rule, ) is expected_result)
def test_answer_source(): value_source_resolver = get_value_source_resolver(answer_store=AnswerStore( [{ "answer_id": "some-answer", "value": "Yes" }]), ) assert (value_source_resolver.resolve({ "source": "answers", "identifier": "some-answer" }) == "Yes")
def rule_evaluator(mock_schema, response_metadata): evaluator = RuleEvaluator( answer_store=AnswerStore(), list_store=ListStore(), metadata={}, response_metadata=response_metadata, schema=mock_schema, location=None, ) return evaluator
def test_answer_value_can_be_escaped(answer_value, escaped_value): value_source_resolver = get_value_source_resolver( answer_store=AnswerStore([{ "answer_id": "some-answer", "value": answer_value, }]), escape_answer_values=True, ) assert (value_source_resolver.resolve({ "source": "answers", "identifier": "some-answer" }) == escaped_value)
def value_source_resolver(mock_schema, response_metadata): resolver = ValueSourceResolver( answer_store=AnswerStore(), list_store=ListStore(), metadata={}, response_metadata=response_metadata, schema=mock_schema, location=None, list_item_id=None, routing_path_block_ids=None, use_default_answer=True, ) return resolver
def test_answer_source_with_list_item_id_no_list_item_selector(): value_source_resolver = get_value_source_resolver( answer_store=AnswerStore([{ "answer_id": "some-answer", "list_item_id": "item-1", "value": "Yes" }]), list_item_id="item-1", ) assert (value_source_resolver.resolve({ "source": "answers", "identifier": "some-answer" }) == "Yes")
def test_answer_source(answer_value, expected_result): rule_evaluator = get_rule_evaluator(answer_store=AnswerStore([{ "answer_id": "some-answer", "value": answer_value }]), ) assert (rule_evaluator.evaluate( rule={ Operator.EQUAL: [{ "source": "answers", "identifier": "some-answer" }, 3] }) is expected_result)
def test_answer_source_with_dict_answer_selector(): value_source_resolver = get_value_source_resolver(answer_store=AnswerStore( [{ "answer_id": "some-answer", "value": { "years": 1, "months": 10 }, }]), ) assert (value_source_resolver.resolve({ "source": "answers", "identifier": "some-answer", "selector": "years", }) == 1)
def test_answer_value_with_selector_can_be_escaped(): value_source_resolver = get_value_source_resolver( answer_store=AnswerStore([{ "answer_id": "some-answer", "value": { "key_1": HTML_CONTENT, "key_2": 1 }, }]), escape_answer_values=True, ) assert (value_source_resolver.resolve({ "source": "answers", "identifier": "some-answer", "selector": "key_1" }) == ESCAPED_CONTENT)
def test_list_item_id_ignored_if_answer_not_in_list_collector_or_repeat(): schema = get_mock_schema() schema.is_repeating_answer = Mock(return_value=False) value_source_resolver = get_value_source_resolver( schema=schema, answer_store=AnswerStore([{ "answer_id": "some-answer", "value": "Yes" }]), list_item_id="item-1", ) assert (value_source_resolver.resolve({ "source": "answers", "identifier": "some-answer" }) == "Yes")
def test_answer_source_with_list_item_selector_location_none(): value_source_resolver = get_value_source_resolver( answer_store=AnswerStore([{ "answer_id": "some-answer", "list_item_id": "item-1", "value": "Yes", }]), location=None, ) with pytest.raises(InvalidLocationException): value_source_resolver.resolve({ "source": "answers", "identifier": "some-answer", "list_item_selector": { "source": "location", "id": "list_item_id" }, })
def test_answer_source_with_list_item_selector_location(): value_source_resolver = get_value_source_resolver( answer_store=AnswerStore([{ "answer_id": "some-answer", "list_item_id": "item-1", "value": "Yes", }]), location=Location(section_id="some-section", block_id="some-block", list_item_id="item-1"), ) assert (value_source_resolver.resolve({ "source": "answers", "identifier": "some-answer", "list_item_selector": { "source": "location", "id": "list_item_id" }, }) == "Yes")
def test_answer_source_with_dict_answer_selector(answer_value, expected_result): rule_evaluator = get_rule_evaluator(answer_store=AnswerStore([{ "answer_id": "some-answer", "value": { "years": answer_value, "months": 10 }, }]), ) assert (rule_evaluator.evaluate( rule={ Operator.EQUAL: [ { "source": "answers", "identifier": "some-answer", "selector": "years", }, 3, ] }) is expected_result)
def test_answer_source_outside_of_repeating_section(): schema = get_mock_schema() schema.is_repeating_answer = Mock(return_value=False) answer_store = AnswerStore([{"answer_id": "some-answer", "value": "Yes"}]) value_source_resolver = get_value_source_resolver( schema=schema, answer_store=answer_store, list_store=ListStore([{ "name": "some-list", "items": get_list_items(3) }]), location=Location(section_id="some-section", block_id="some-block", list_item_id="item-1"), ) assert (value_source_resolver.resolve({ "source": "answers", "identifier": "some-answer" }) == "Yes")
def test_answer_source_with_list_item_selector_list_first_item(): value_source_resolver = get_value_source_resolver( answer_store=AnswerStore([{ "answer_id": "some-answer", "list_item_id": "item-1", "value": "Yes", }]), list_store=ListStore([{ "name": "some-list", "items": get_list_items(3) }]), ) assert (value_source_resolver.resolve({ "source": "answers", "identifier": "some-answer", "list_item_selector": { "source": "list", "id": "some-list", "id_selector": "first", }, }) == "Yes")
def test_answer_source_default_answer_used_when_no_answer( comparison_value, expected_result): schema = get_mock_schema() schema.get_default_answer = Mock( return_value=Answer(answer_id="answer-that-does-not-exist", value=3)) rule_evaluator = get_rule_evaluator( schema=schema, answer_store=AnswerStore([{ "answer_id": "some-answer", "value": "No" }]), ) assert (rule_evaluator.evaluate( rule={ Operator.EQUAL: [ { "source": "answers", "identifier": "answer-that-does-not-exist" }, comparison_value, ] }) is expected_result)
def test_answer_source_outside_of_repeating_section(): schema = get_mock_schema() schema.is_repeating_answer = Mock(return_value=False) answer_store = AnswerStore([{"answer_id": "some-answer", "value": "Yes"}]) rule_evaluator = get_rule_evaluator( schema=schema, answer_store=answer_store, location=Location(section_id="some-section", block_id="some-block", list_item_id="item-1"), ) assert (rule_evaluator.evaluate( rule={ Operator.EQUAL: [ { "source": "answers", "identifier": "some-answer" }, "Yes", ] }) is True)
def convert_answers_to_payload_0_0_1( metadata: MetadataType, response_metadata: MetadataType, answer_store: AnswerStore, list_store: ListStore, schema: QuestionnaireSchema, full_routing_path: RoutingPath, ) -> OrderedDict[str, Any]: """ Convert answers into the data format below list_item_id bound answers are not currently supported 'data': { '001': '01-01-2016', '002': '30-03-2016' } :param metadata: questionnaire metadata :param response_metadata: response metadata :param answer_store: questionnaire answers :param list_store: list store :param schema: QuestionnaireSchema class with populated schema json :param full_routing_path: a list of section routing paths followed in the questionnaire :return: data in a formatted form """ data = OrderedDict() for routing_path in full_routing_path: for block_id in routing_path: answer_ids = schema.get_answer_ids_for_block(block_id) answers_in_block = answer_store.get_answers_by_answer_id( answer_ids, routing_path.list_item_id) for answer_in_block in answers_in_block: answer_schema = None block = schema.get_block_for_answer_id( answer_in_block.answer_id) current_location = Location( block_id=block_id, section_id=routing_path.section_id, list_item_id=routing_path.list_item_id, ) question = choose_question_to_display( block, schema, metadata, response_metadata, answer_store, list_store, current_location=current_location, ) for answer in question["answers"]: if answer["id"] == answer_in_block.answer_id: answer_schema = answer value = answer_in_block.value if answer_schema is not None and value is not None: if answer_schema["type"] == "Checkbox": data.update( _get_checkbox_answer_data( answer_store, answer_schema, value # type: ignore )) elif "q_code" in answer_schema: answer_data = _encode_value(value) if answer_data is not None: data[answer_schema[ "q_code"]] = _format_downstream_answer( answer_schema["type"], answer_in_block.value, answer_data, ) return data