From 37a3c75dc4da96b421d3b65834bcc354d3a1e4ab Mon Sep 17 00:00:00 2001 From: Nupur Khare <78532321+nupur-khare@users.noreply.github.com> Date: Fri, 10 Nov 2023 07:10:25 +0530 Subject: [PATCH] File upload download fix (#1073) * 1. Fixed Multiflow stories upload download issue where it was skipping intents, utterances and actions already present in the bot. 2. Added test cases 3. Fixed test cases. --------- Co-authored-by: Nupur Khare --- kairon/importer/validator/file_validator.py | 3 + kairon/shared/utils.py | 3 +- .../valid_with_multiflow/actions.yml | 12 +++ .../valid_with_multiflow/data/nlu.yml | 13 ++++ .../valid_with_multiflow/domain.yml | 26 +++++++ .../multiflow_stories.yml | 74 +++++++++++++++++++ tests/unit_test/events/events_test.py | 10 +-- .../unit_test/validator/data_importer_test.py | 15 ++-- .../validator/training_data_validator_test.py | 3 +- 9 files changed, 145 insertions(+), 14 deletions(-) create mode 100644 tests/testing_data/multiflow_stories/valid_with_multiflow/actions.yml diff --git a/kairon/importer/validator/file_validator.py b/kairon/importer/validator/file_validator.py index ff60ffa93..2e29827a9 100644 --- a/kairon/importer/validator/file_validator.py +++ b/kairon/importer/validator/file_validator.py @@ -349,6 +349,9 @@ def verify_utterance_and_actions_in_multiflow_stories(self, raise_exception: boo utterances = events elif step_type != StoryStepType.intent.value and step_type != StoryStepType.slot.value: actions.extend(events) + if step_type == StoryStepType.form_action.value: + user_actions.update(event for event in events + if event in self.domain.form_names and f"validate_{event}" in user_actions) for utterance in utterances: if utterance not in user_actions: diff --git a/kairon/shared/utils.py b/kairon/shared/utils.py index 974acc4e1..948d2fe2b 100644 --- a/kairon/shared/utils.py +++ b/kairon/shared/utils.py @@ -1940,7 +1940,8 @@ def get_names_for_events(graph: DiGraph, step_type: str): def get_step_name_for_multiflow_stories(story_graph: list, step_type: str): name = set() for graph in story_graph: - name = StoryValidator.get_names_for_events(graph, step_type) + events = StoryValidator.get_names_for_events(graph, step_type) + name.update(events) return name diff --git a/tests/testing_data/multiflow_stories/valid_with_multiflow/actions.yml b/tests/testing_data/multiflow_stories/valid_with_multiflow/actions.yml new file mode 100644 index 000000000..37a7e3030 --- /dev/null +++ b/tests/testing_data/multiflow_stories/valid_with_multiflow/actions.yml @@ -0,0 +1,12 @@ +form_validation_action: +- invalid_response: Please ensure that you attach a valid image file. This upload + only supports image file. + is_required: true + name: validate_form_image + slot: var_image1 + slot_set: + type: slot + value: image + valid_response: '' + validation_semantic: if (slot['image'] == null) { return false; } else { return + true; } diff --git a/tests/testing_data/multiflow_stories/valid_with_multiflow/data/nlu.yml b/tests/testing_data/multiflow_stories/valid_with_multiflow/data/nlu.yml index 045943b25..390041019 100644 --- a/tests/testing_data/multiflow_stories/valid_with_multiflow/data/nlu.yml +++ b/tests/testing_data/multiflow_stories/valid_with_multiflow/data/nlu.yml @@ -16,3 +16,16 @@ nlu: - yes - Yeah! - It's a yes +- intent: query + examples: | + - I have a doubt + - Doubt! + - I have a question +- intent: priority + examples: | + - low + - priority is low +- intent: food + examples: | + - food suggestions + - Hungry! diff --git a/tests/testing_data/multiflow_stories/valid_with_multiflow/domain.yml b/tests/testing_data/multiflow_stories/valid_with_multiflow/domain.yml index eef64e918..8e79f2a43 100644 --- a/tests/testing_data/multiflow_stories/valid_with_multiflow/domain.yml +++ b/tests/testing_data/multiflow_stories/valid_with_multiflow/domain.yml @@ -11,6 +11,20 @@ intents: use_entities: true - affirm: use_entities: true +- query: + use_entities: true +- priority: + use_entities: true +- food: + use_entities: true +entities: +- var_image1 +slots: + var_image1: + type: rasa.shared.core.slots.TextSlot + initial_value: null + auto_fill: false + influence_conversation: false responses: utter_goodbye: - text: Bye @@ -18,12 +32,24 @@ responses: - text: Hey! How are you? utter_affirm: - text: Your answer is yes! + utter_query: + - text: Your answer is yes! + utter_priority: + - text: Your answer is yes! + utter_food: + - text: Your answer is yes! utter_default: - text: Can you rephrase! actions: +- validate_form_image - action_say_hello - action_say_goodbye - utter_greet - utter_goodbye - utter_affirm +forms: + form_image: + required_slots: + var_image1: + - type: from_text diff --git a/tests/testing_data/multiflow_stories/valid_with_multiflow/multiflow_stories.yml b/tests/testing_data/multiflow_stories/valid_with_multiflow/multiflow_stories.yml index 4228211ea..d705ba98e 100644 --- a/tests/testing_data/multiflow_stories/valid_with_multiflow/multiflow_stories.yml +++ b/tests/testing_data/multiflow_stories/valid_with_multiflow/multiflow_stories.yml @@ -63,3 +63,77 @@ multiflow_story: start_checkpoints: [STORY_START] end_checkpoints: [] template_type: CUSTOM +- block_name: mf_two_w + events: + - step: + name: query + type: INTENT + node_id: "7" + component_id: "61m96mPvb2VexybDeVg1dLyH" + connections: + - name: utter_query + type: BOT + node_id: "8" + component_id: "61uaImwNrsJI1pVphc0mZh20" + - step: + name: utter_query + type: BOT + node_id: "8" + component_id: "61uaImwNrsJI1pVphc0mZh20" + connections: + - name: priority + type: INTENT + node_id: "9" + component_id: "62By0VXVazUNDNPqkr5vRRzm" + - name: food + type: INTENT + node_id: "10" + component_id: "62N9BCfSKVYOKoSixGhWDRHC" + - step: + name: priority + type: INTENT + node_id: "9" + component_id: "62By0VXVazUNDNPqkr5vRRzm" + connections: + - name: utter_priority + type: BOT + node_id: "11" + component_id: "62uzXd9Pj1q9tEbVBkMuVn3o" + - step: + name: utter_priority + type: BOT + node_id: "11" + component_id: "62uzXd9Pj1q9tEbVBkMuVn3o" + connections: null + - step: + name: food + type: INTENT + node_id: "10" + component_id: "62N9BCfSKVYOKoSixGhWDRHC" + connections: + - name: form_image + type: FORM_ACTION + node_id: "12" + component_id: "62ib6tlbgICth8vBSwSYFvbS" + - step: + name: form_image + type: FORM_ACTION + node_id: "12" + component_id: "62ib6tlbgICth8vBSwSYFvbS" + connections: + - name: utter_food + type: BOT + node_id: "13" + component_id: "62ib6tlbgIGth8vBSwSYFoPS" + - step: + name: utter_food + type: BOT + node_id: "13" + component_id: "62ib6tlbgIGth8vBSwSYFoPS" + connections: null + metadata: + - node_id: "13" + flow_type: STORY + start_checkpoints: [STORY_START] + end_checkpoints: [] + template_type: CUSTOM diff --git a/tests/unit_test/events/events_test.py b/tests/unit_test/events/events_test.py index a9c97538d..346598106 100644 --- a/tests/unit_test/events/events_test.py +++ b/tests/unit_test/events/events_test.py @@ -543,11 +543,11 @@ def _path(*args, **kwargs): assert logs[0]['event_status'] == EVENT_STATUS.COMPLETED.value assert len(mongo_processor.fetch_stories(bot)) == 2 - assert len(list(mongo_processor.fetch_training_examples(bot))) == 10 - assert len(list(mongo_processor.fetch_responses(bot))) == 4 - assert len(mongo_processor.fetch_actions(bot)) == 2 + assert len(list(mongo_processor.fetch_training_examples(bot))) == 17 + assert len(list(mongo_processor.fetch_responses(bot))) == 7 + assert len(mongo_processor.fetch_actions(bot)) == 3 assert len(mongo_processor.fetch_rule_block_names(bot)) == 3 - assert len(mongo_processor.fetch_multiflow_stories(bot)) == 1 + assert len(mongo_processor.fetch_multiflow_stories(bot)) == 2 def test_trigger_data_importer_stories_only(self, monkeypatch, get_training_data): bot = 'test_trigger_data_importer_stories_only' @@ -1138,7 +1138,7 @@ def test_trigger_model_testing_event_2(self): assert not os.path.exists(os.path.join('./testing_data', bot)) def test_trigger_history_deletion_for_bot(self): - from datetime import datetime, timedelta + from datetime import datetime bot = 'test_events_bot' user = 'test_user' till_date = datetime.utcnow().date() diff --git a/tests/unit_test/validator/data_importer_test.py b/tests/unit_test/validator/data_importer_test.py index c99289a6e..c1773661c 100644 --- a/tests/unit_test/validator/data_importer_test.py +++ b/tests/unit_test/validator/data_importer_test.py @@ -2,14 +2,15 @@ import shutil import tempfile import uuid + import pytest from mongoengine import connect -from kairon.shared.utils import Utility -from kairon.shared.data.constant import REQUIREMENTS -from kairon.shared.data.processor import MongoProcessor from kairon.exceptions import AppException from kairon.importer.data_importer import DataImporter +from kairon.shared.data.constant import REQUIREMENTS +from kairon.shared.data.processor import MongoProcessor +from kairon.shared.utils import Utility def pytest_namespace(): @@ -135,11 +136,11 @@ async def test_import_data_with_multiflow(self): assert 'greet' in processor.fetch_intents(bot) assert 'deny' in processor.fetch_intents(bot) assert len(processor.fetch_stories(bot)) == 2 - assert len(list(processor.fetch_training_examples(bot))) == 10 - assert len(list(processor.fetch_responses(bot))) == 5 - assert len(processor.fetch_actions(bot)) == 2 + assert len(list(processor.fetch_training_examples(bot))) == 17 + assert len(list(processor.fetch_responses(bot))) == 8 + assert len(processor.fetch_actions(bot)) == 3 assert len(processor.fetch_rule_block_names(bot)) == 4 - assert len(processor.fetch_multiflow_stories(bot)) == 1 + assert len(processor.fetch_multiflow_stories(bot)) == 2 @pytest.mark.asyncio async def test_import_data_append(self): diff --git a/tests/unit_test/validator/training_data_validator_test.py b/tests/unit_test/validator/training_data_validator_test.py index 1ed7fc9ad..ec1bdf760 100644 --- a/tests/unit_test/validator/training_data_validator_test.py +++ b/tests/unit_test/validator/training_data_validator_test.py @@ -272,10 +272,11 @@ async def test_validate_valid_training_data_with_multiflow_stories(self): nlu_path = 'tests/testing_data/multiflow_stories/valid_with_multiflow/data' config_path = 'tests/testing_data/multiflow_stories/valid_with_multiflow/config.yml' validator = await TrainingDataValidator.from_training_files(nlu_path, domain_path, config_path, root) - validator.validate_training_data() + validator.validate_training_data(False) assert not validator.summary.get('intents') assert not validator.summary.get('utterances') assert not validator.summary.get('stories') + assert not validator.summary.get('form_validation_actions') assert not validator.summary.get('multiflow_stories') assert not validator.summary.get('training_examples') assert not validator.summary.get('domain')